@tatchi-xyz/sdk 0.47.0 → 0.49.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 (200) hide show
  1. package/dist/cjs/core/TatchiPasskey/actions.js +8 -6
  2. package/dist/cjs/core/TatchiPasskey/actions.js.map +1 -1
  3. package/dist/cjs/core/TatchiPasskey/login.js +11 -1
  4. package/dist/cjs/core/TatchiPasskey/login.js.map +1 -1
  5. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/index.js +36 -2
  6. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/index.js.map +1 -1
  7. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/index.js +33 -13
  8. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/index.js.map +1 -1
  9. package/dist/cjs/core/WebAuthnManager/WebAuthnFallbacks/safari-fallbacks.js +26 -8
  10. package/dist/cjs/core/WebAuthnManager/WebAuthnFallbacks/safari-fallbacks.js.map +1 -1
  11. package/dist/cjs/core/WebAuthnManager/index.js +19 -0
  12. package/dist/cjs/core/WebAuthnManager/index.js.map +1 -1
  13. package/dist/cjs/core/defaultConfigs.js.map +1 -1
  14. package/dist/cjs/core/threshold/thresholdEd25519AuthSession.js +25 -2
  15. package/dist/cjs/core/threshold/thresholdEd25519AuthSession.js.map +1 -1
  16. package/dist/cjs/core/threshold/thresholdSessionPolicy.js +5 -7
  17. package/dist/cjs/core/threshold/thresholdSessionPolicy.js.map +1 -1
  18. package/dist/cjs/react/src/core/TatchiPasskey/actions.js +8 -6
  19. package/dist/cjs/react/src/core/TatchiPasskey/actions.js.map +1 -1
  20. package/dist/cjs/react/src/core/TatchiPasskey/login.js +11 -1
  21. package/dist/cjs/react/src/core/TatchiPasskey/login.js.map +1 -1
  22. package/dist/cjs/react/src/core/WebAuthnManager/SignerWorkerManager/index.js +36 -2
  23. package/dist/cjs/react/src/core/WebAuthnManager/SignerWorkerManager/index.js.map +1 -1
  24. package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/index.js +33 -13
  25. package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/index.js.map +1 -1
  26. package/dist/cjs/react/src/core/WebAuthnManager/WebAuthnFallbacks/safari-fallbacks.js +26 -8
  27. package/dist/cjs/react/src/core/WebAuthnManager/WebAuthnFallbacks/safari-fallbacks.js.map +1 -1
  28. package/dist/cjs/react/src/core/WebAuthnManager/index.js +19 -0
  29. package/dist/cjs/react/src/core/WebAuthnManager/index.js.map +1 -1
  30. package/dist/cjs/react/src/core/defaultConfigs.js.map +1 -1
  31. package/dist/cjs/react/src/core/threshold/thresholdEd25519AuthSession.js +25 -2
  32. package/dist/cjs/react/src/core/threshold/thresholdEd25519AuthSession.js.map +1 -1
  33. package/dist/cjs/react/src/core/threshold/thresholdSessionPolicy.js +5 -7
  34. package/dist/cjs/react/src/core/threshold/thresholdSessionPolicy.js.map +1 -1
  35. package/dist/cjs/server/core/SessionService.js +26 -2
  36. package/dist/cjs/server/core/SessionService.js.map +1 -1
  37. package/dist/cjs/server/core/ThresholdService/ThresholdSigningService.js +90 -10
  38. package/dist/cjs/server/core/ThresholdService/ThresholdSigningService.js.map +1 -1
  39. package/dist/cjs/server/core/ThresholdService/createThresholdSigningService.js +1 -0
  40. package/dist/cjs/server/core/ThresholdService/createThresholdSigningService.js.map +1 -1
  41. package/dist/cjs/server/core/ThresholdService/stores/AuthSessionStore.js +92 -1
  42. package/dist/cjs/server/core/ThresholdService/stores/AuthSessionStore.js.map +1 -1
  43. package/dist/cjs/server/core/ThresholdService/stores/CloudflareDurableObjectStore.js +284 -0
  44. package/dist/cjs/server/core/ThresholdService/stores/CloudflareDurableObjectStore.js.map +1 -0
  45. package/dist/cjs/server/core/ThresholdService/stores/KeyStore.js +8 -1
  46. package/dist/cjs/server/core/ThresholdService/stores/KeyStore.js.map +1 -1
  47. package/dist/cjs/server/core/ThresholdService/stores/SessionStore.js +8 -1
  48. package/dist/cjs/server/core/ThresholdService/stores/SessionStore.js.map +1 -1
  49. package/dist/cjs/server/core/ThresholdService/validation.js +28 -0
  50. package/dist/cjs/server/core/ThresholdService/validation.js.map +1 -1
  51. package/dist/cjs/server/core/config.js +14 -2
  52. package/dist/cjs/server/core/config.js.map +1 -1
  53. package/dist/cjs/server/core/defaultConfigsServer.js +15 -0
  54. package/dist/cjs/server/core/defaultConfigsServer.js.map +1 -0
  55. package/dist/cjs/server/index.js +6 -0
  56. package/dist/cjs/server/router/cloudflare.js +300 -73
  57. package/dist/cjs/server/router/cloudflare.js.map +1 -1
  58. package/dist/cjs/server/router/express.js +94 -2
  59. package/dist/cjs/server/router/express.js.map +1 -1
  60. package/dist/cjs/server/sdk/src/core/defaultConfigs.js.map +1 -1
  61. package/dist/esm/core/TatchiPasskey/actions.js +8 -6
  62. package/dist/esm/core/TatchiPasskey/actions.js.map +1 -1
  63. package/dist/esm/core/TatchiPasskey/login.js +11 -1
  64. package/dist/esm/core/TatchiPasskey/login.js.map +1 -1
  65. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/index.js +36 -2
  66. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/index.js.map +1 -1
  67. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/index.js +33 -13
  68. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/index.js.map +1 -1
  69. package/dist/esm/core/WebAuthnManager/WebAuthnFallbacks/safari-fallbacks.js +26 -8
  70. package/dist/esm/core/WebAuthnManager/WebAuthnFallbacks/safari-fallbacks.js.map +1 -1
  71. package/dist/esm/core/WebAuthnManager/index.js +19 -0
  72. package/dist/esm/core/WebAuthnManager/index.js.map +1 -1
  73. package/dist/esm/core/defaultConfigs.js.map +1 -1
  74. package/dist/esm/core/threshold/thresholdEd25519AuthSession.js +25 -2
  75. package/dist/esm/core/threshold/thresholdEd25519AuthSession.js.map +1 -1
  76. package/dist/esm/core/threshold/thresholdSessionPolicy.js +5 -7
  77. package/dist/esm/core/threshold/thresholdSessionPolicy.js.map +1 -1
  78. package/dist/esm/react/src/core/TatchiPasskey/actions.js +8 -6
  79. package/dist/esm/react/src/core/TatchiPasskey/actions.js.map +1 -1
  80. package/dist/esm/react/src/core/TatchiPasskey/login.js +11 -1
  81. package/dist/esm/react/src/core/TatchiPasskey/login.js.map +1 -1
  82. package/dist/esm/react/src/core/WebAuthnManager/SignerWorkerManager/index.js +36 -2
  83. package/dist/esm/react/src/core/WebAuthnManager/SignerWorkerManager/index.js.map +1 -1
  84. package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/index.js +33 -13
  85. package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/index.js.map +1 -1
  86. package/dist/esm/react/src/core/WebAuthnManager/WebAuthnFallbacks/safari-fallbacks.js +26 -8
  87. package/dist/esm/react/src/core/WebAuthnManager/WebAuthnFallbacks/safari-fallbacks.js.map +1 -1
  88. package/dist/esm/react/src/core/WebAuthnManager/index.js +19 -0
  89. package/dist/esm/react/src/core/WebAuthnManager/index.js.map +1 -1
  90. package/dist/esm/react/src/core/defaultConfigs.js.map +1 -1
  91. package/dist/esm/react/src/core/threshold/thresholdEd25519AuthSession.js +25 -2
  92. package/dist/esm/react/src/core/threshold/thresholdEd25519AuthSession.js.map +1 -1
  93. package/dist/esm/react/src/core/threshold/thresholdSessionPolicy.js +5 -7
  94. package/dist/esm/react/src/core/threshold/thresholdSessionPolicy.js.map +1 -1
  95. package/dist/esm/sdk/{collectAuthenticationCredentialForVrfChallenge-C9p90e5Z.js → collectAuthenticationCredentialForVrfChallenge-DStr-xUe.js} +27 -9
  96. package/dist/esm/sdk/collectAuthenticationCredentialForVrfChallenge-DStr-xUe.js.map +1 -0
  97. package/dist/esm/sdk/{collectAuthenticationCredentialForVrfChallenge-DqzPzwvU.js → collectAuthenticationCredentialForVrfChallenge-DziJEs0K.js} +1 -1
  98. package/dist/esm/sdk/{createAdapters-B26G-xfI.js → createAdapters-Cd82ruyc.js} +1 -1
  99. package/dist/esm/sdk/{createAdapters-NCOOvHKB.js → createAdapters-DQu8yecX.js} +2 -2
  100. package/dist/esm/sdk/{createAdapters-NCOOvHKB.js.map → createAdapters-DQu8yecX.js.map} +1 -1
  101. package/dist/esm/sdk/{emailRecovery-D7sF0Qhr.js → emailRecovery-Cdrq7VHg.js} +3 -3
  102. package/dist/esm/sdk/{getDeviceNumber-aa3xNnaG.js → getDeviceNumber-DDzTSBNN.js} +2 -2
  103. package/dist/esm/sdk/{getDeviceNumber-aa3xNnaG.js.map → getDeviceNumber-DDzTSBNN.js.map} +1 -1
  104. package/dist/esm/sdk/{linkDevice-91LmHy2K.js → linkDevice-BAu6ztGb.js} +3 -3
  105. package/dist/esm/sdk/{localOnly-DKksw6Zt.js → localOnly-B2bo070R.js} +3 -3
  106. package/dist/esm/sdk/{localOnly-DKksw6Zt.js.map → localOnly-B2bo070R.js.map} +1 -1
  107. package/dist/esm/sdk/{localOnly-CQOX1pqN.js → localOnly-DO0wqplt.js} +4 -4
  108. package/dist/esm/sdk/{login-D9l_OAyu.js → login-BrbG-W7_.js} +43 -12
  109. package/dist/esm/sdk/offline-export-app.js +117 -26
  110. package/dist/esm/sdk/offline-export-app.js.map +1 -1
  111. package/dist/esm/sdk/{registration-CRD5afRG.js → registration-BOwlGsk5.js} +4 -4
  112. package/dist/esm/sdk/{registration-CGNNpYB4.js → registration-C3HGqlZK.js} +3 -3
  113. package/dist/esm/sdk/{registration-CGNNpYB4.js.map → registration-C3HGqlZK.js.map} +1 -1
  114. package/dist/esm/sdk/{router-DbB37GdZ.js → router-BUUlXomz.js} +1 -1
  115. package/dist/esm/sdk/{safari-fallbacks-BcMFntiP.js → safari-fallbacks-DE4sjlRl.js} +26 -8
  116. package/dist/esm/sdk/{scanDevice-TKW-Q6oV.js → scanDevice-DXvt5czx.js} +3 -3
  117. package/dist/esm/sdk/{touchIdPrompt-JPhrOx8o.js → touchIdPrompt-mO8X1Sai.js} +1 -1
  118. package/dist/esm/sdk/{transactions-Bi6hJqg0.js → transactions-BTFYyLh-.js} +4 -4
  119. package/dist/esm/sdk/{transactions-CuIEW7P-.js → transactions-DO83NFx7.js} +4 -4
  120. package/dist/esm/sdk/{transactions-CuIEW7P-.js.map → transactions-DO83NFx7.js.map} +1 -1
  121. package/dist/esm/sdk/wallet-iframe-host.js +110 -35
  122. package/dist/esm/server/core/SessionService.js +26 -2
  123. package/dist/esm/server/core/SessionService.js.map +1 -1
  124. package/dist/esm/server/core/ThresholdService/ThresholdSigningService.js +90 -10
  125. package/dist/esm/server/core/ThresholdService/ThresholdSigningService.js.map +1 -1
  126. package/dist/esm/server/core/ThresholdService/createThresholdSigningService.js +1 -0
  127. package/dist/esm/server/core/ThresholdService/createThresholdSigningService.js.map +1 -1
  128. package/dist/esm/server/core/ThresholdService/stores/AuthSessionStore.js +93 -2
  129. package/dist/esm/server/core/ThresholdService/stores/AuthSessionStore.js.map +1 -1
  130. package/dist/esm/server/core/ThresholdService/stores/CloudflareDurableObjectStore.js +284 -0
  131. package/dist/esm/server/core/ThresholdService/stores/CloudflareDurableObjectStore.js.map +1 -0
  132. package/dist/esm/server/core/ThresholdService/stores/KeyStore.js +9 -2
  133. package/dist/esm/server/core/ThresholdService/stores/KeyStore.js.map +1 -1
  134. package/dist/esm/server/core/ThresholdService/stores/SessionStore.js +9 -2
  135. package/dist/esm/server/core/ThresholdService/stores/SessionStore.js.map +1 -1
  136. package/dist/esm/server/core/ThresholdService/validation.js +28 -1
  137. package/dist/esm/server/core/ThresholdService/validation.js.map +1 -1
  138. package/dist/esm/server/core/config.js +14 -2
  139. package/dist/esm/server/core/config.js.map +1 -1
  140. package/dist/esm/server/core/defaultConfigsServer.js +10 -0
  141. package/dist/esm/server/core/defaultConfigsServer.js.map +1 -0
  142. package/dist/esm/server/index.js +2 -1
  143. package/dist/esm/server/router/cloudflare.js +300 -74
  144. package/dist/esm/server/router/cloudflare.js.map +1 -1
  145. package/dist/esm/server/router/express.js +94 -2
  146. package/dist/esm/server/router/express.js.map +1 -1
  147. package/dist/esm/server/sdk/src/core/defaultConfigs.js.map +1 -1
  148. package/dist/esm/wasm_signer_worker/pkg/wasm_signer_worker_bg.wasm +0 -0
  149. package/dist/esm/wasm_vrf_worker/pkg/wasm_vrf_worker_bg.wasm +0 -0
  150. package/dist/types/src/__tests__/helpers/thresholdEd25519TestUtils.d.ts.map +1 -1
  151. package/dist/types/src/core/TatchiPasskey/actions.d.ts.map +1 -1
  152. package/dist/types/src/core/TatchiPasskey/login.d.ts.map +1 -1
  153. package/dist/types/src/core/WebAuthnManager/SignerWorkerManager/index.d.ts +11 -0
  154. package/dist/types/src/core/WebAuthnManager/SignerWorkerManager/index.d.ts.map +1 -1
  155. package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/index.d.ts +7 -0
  156. package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/index.d.ts.map +1 -1
  157. package/dist/types/src/core/WebAuthnManager/WebAuthnFallbacks/safari-fallbacks.d.ts.map +1 -1
  158. package/dist/types/src/core/WebAuthnManager/index.d.ts +9 -0
  159. package/dist/types/src/core/WebAuthnManager/index.d.ts.map +1 -1
  160. package/dist/types/src/core/defaultConfigs.d.ts +0 -2
  161. package/dist/types/src/core/defaultConfigs.d.ts.map +1 -1
  162. package/dist/types/src/core/threshold/thresholdEd25519AuthSession.d.ts +1 -0
  163. package/dist/types/src/core/threshold/thresholdEd25519AuthSession.d.ts.map +1 -1
  164. package/dist/types/src/core/threshold/thresholdSessionPolicy.d.ts +0 -2
  165. package/dist/types/src/core/threshold/thresholdSessionPolicy.d.ts.map +1 -1
  166. package/dist/types/src/server/core/SessionService.d.ts +2 -1
  167. package/dist/types/src/server/core/SessionService.d.ts.map +1 -1
  168. package/dist/types/src/server/core/ThresholdService/ThresholdSigningService.d.ts +3 -1
  169. package/dist/types/src/server/core/ThresholdService/ThresholdSigningService.d.ts.map +1 -1
  170. package/dist/types/src/server/core/ThresholdService/createThresholdSigningService.d.ts.map +1 -1
  171. package/dist/types/src/server/core/ThresholdService/stores/AuthSessionStore.d.ts +15 -0
  172. package/dist/types/src/server/core/ThresholdService/stores/AuthSessionStore.d.ts.map +1 -1
  173. package/dist/types/src/server/core/ThresholdService/stores/CloudflareDurableObjectStore.d.ts +62 -0
  174. package/dist/types/src/server/core/ThresholdService/stores/CloudflareDurableObjectStore.d.ts.map +1 -0
  175. package/dist/types/src/server/core/ThresholdService/stores/KeyStore.d.ts.map +1 -1
  176. package/dist/types/src/server/core/ThresholdService/stores/SessionStore.d.ts.map +1 -1
  177. package/dist/types/src/server/core/ThresholdService/validation.d.ts +25 -0
  178. package/dist/types/src/server/core/ThresholdService/validation.d.ts.map +1 -1
  179. package/dist/types/src/server/core/config.d.ts.map +1 -1
  180. package/dist/types/src/server/core/defaultConfigsServer.d.ts +6 -0
  181. package/dist/types/src/server/core/defaultConfigsServer.d.ts.map +1 -0
  182. package/dist/types/src/server/core/types.d.ts +32 -1
  183. package/dist/types/src/server/core/types.d.ts.map +1 -1
  184. package/dist/types/src/server/index.d.ts +1 -0
  185. package/dist/types/src/server/index.d.ts.map +1 -1
  186. package/dist/types/src/server/router/cloudflare/durableObjects/thresholdEd25519Store.d.ts +18 -0
  187. package/dist/types/src/server/router/cloudflare/durableObjects/thresholdEd25519Store.d.ts.map +1 -0
  188. package/dist/types/src/server/router/cloudflare/routes/thresholdEd25519.d.ts.map +1 -1
  189. package/dist/types/src/server/router/cloudflare-adaptor.d.ts +1 -0
  190. package/dist/types/src/server/router/cloudflare-adaptor.d.ts.map +1 -1
  191. package/dist/types/src/server/router/commonRouterUtils.d.ts +2 -0
  192. package/dist/types/src/server/router/commonRouterUtils.d.ts.map +1 -1
  193. package/dist/types/src/server/router/express/routes/thresholdEd25519.d.ts.map +1 -1
  194. package/dist/types/src/server/router/relay.d.ts +2 -0
  195. package/dist/types/src/server/router/relay.d.ts.map +1 -1
  196. package/dist/workers/wasm_signer_worker_bg.wasm +0 -0
  197. package/dist/workers/wasm_vrf_worker_bg.wasm +0 -0
  198. package/dist/workers/web3authn-signer.worker.js +2 -2
  199. package/package.json +1 -1
  200. package/dist/esm/sdk/collectAuthenticationCredentialForVrfChallenge-C9p90e5Z.js.map +0 -1
@@ -348,12 +348,14 @@ async function signTransactionsWithActionsInternal({ context, nearAccountId, tra
348
348
  status: require_sdkSentEvents.ActionStatus.PROGRESS,
349
349
  message: "Signing transaction..."
350
350
  });
351
- if (progressEvent.phase === require_sdkSentEvents.ActionPhase.STEP_6_TRANSACTION_SIGNING_COMPLETE) onEvent?.({
352
- step: 6,
353
- phase: require_sdkSentEvents.ActionPhase.STEP_6_TRANSACTION_SIGNING_COMPLETE,
354
- status: require_sdkSentEvents.ActionStatus.SUCCESS,
355
- message: "Transaction signed successfully"
356
- });
351
+ if (progressEvent.phase === require_sdkSentEvents.ActionPhase.STEP_6_TRANSACTION_SIGNING_COMPLETE) {
352
+ if (progressEvent.status !== require_sdkSentEvents.ActionStatus.ERROR) onEvent?.({
353
+ step: 6,
354
+ phase: require_sdkSentEvents.ActionPhase.STEP_6_TRANSACTION_SIGNING_COMPLETE,
355
+ status: require_sdkSentEvents.ActionStatus.SUCCESS,
356
+ message: progressEvent.message || "Transaction signed successfully"
357
+ });
358
+ }
357
359
  onEvent(progressEvent);
358
360
  } : void 0
359
361
  });
@@ -1 +1 @@
1
- {"version":3,"file":"actions.js","names":["mergeSignerMode","error: unknown","toError","ActionPhase","ActionStatus","st: any","getNearShortErrorMessage","result: ActionResult","txResults: ActionResult[]","transactionInputsWasm: TransactionInputWasm[]","toActionArgsWasm","error: any","ActionType"],"sources":["../../../../src/core/TatchiPasskey/actions.ts"],"sourcesContent":["import { ActionType, toActionArgsWasm } from '../types/actions';\nimport type {\n ActionHooksOptions,\n ExecutionWaitOption,\n SendTransactionHooksOptions,\n SignAndSendTransactionHooksOptions,\n SignTransactionHooksOptions,\n} from '../types/sdkSentEvents';\nimport type { ActionResult, SignTransactionResult } from '../types/tatchi';\nimport type { TxExecutionStatus } from '@near-js/types';\nimport type { ActionArgs, TransactionInput, TransactionInputWasm } from '../types/actions';\nimport { type ConfirmationConfig, type SignerMode, mergeSignerMode } from '../types/signer-worker';\nimport type { PasskeyManagerContext } from './index';\nimport type { SignedTransaction } from '../NearClient';\nimport type { AccountId } from '../types/accountIds';\nimport { ActionPhase, ActionStatus, type ActionSSEEvent, type onProgressEvents } from '../types/sdkSentEvents';\nimport { toError, getNearShortErrorMessage } from '../../utils/errors';\n\n/**\n * executeAction signs a single transaction (with actions[]) to a single receiver.\n * If you want to sign multiple transactions to different receivers,\n * use signTransactionsWithActions() instead.\n *\n * @param context - TatchiPasskey context\n * @param nearAccountId - NEAR account ID to sign transactions with\n * @param actionArgs - Action arguments to sign transactions with\n * @param options - Options for the action\n * @returns Promise resolving to the action result\n */\nexport async function executeAction(args: {\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n receiverId: AccountId,\n actionArgs: ActionArgs | ActionArgs[],\n options: ActionHooksOptions,\n}): Promise<ActionResult> {\n try {\n const signerMode = mergeSignerMode(getBaseSignerMode(args.context), args.options?.signerMode);\n // Thread optional per-call confirmation override when provided; otherwise\n // user preferences determine the confirmation behavior.\n return executeActionInternal({\n context: args.context,\n nearAccountId: args.nearAccountId,\n receiverId: args.receiverId,\n actionArgs: args.actionArgs,\n signerMode,\n options: args.options,\n confirmationConfigOverride: args.options.confirmationConfig\n });\n } catch (error: unknown) {\n throw toError(error);\n }\n}\n\n// Execution plan types for broadcasting multiple transactions\n// Helper: parse executionWait (only sequential for now)\nfunction parseExecutionWait(options?: SignAndSendTransactionHooksOptions): { waitUntil?: TxExecutionStatus } {\n return { waitUntil: options?.waitUntil };\n}\n\n/**\n * Signs multiple transactions with actions, and broadcasts them\n *\n * @param context - TatchiPasskey context\n * @param nearAccountId - NEAR account ID to sign transactions with\n * @param transactionInput - Transaction input to sign\n * @param options - Options for the action\n * @returns Promise resolving to the action result\n */\nexport async function signAndSendTransactions(args: {\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n transactionInputs: TransactionInput[],\n options: SignAndSendTransactionHooksOptions,\n}): Promise<ActionResult[]> {\n const signerMode = mergeSignerMode(getBaseSignerMode(args.context), args.options?.signerMode);\n return signAndSendTransactionsInternal({\n context: args.context,\n nearAccountId: args.nearAccountId,\n transactionInputs: args.transactionInputs,\n signerMode,\n options: args.options,\n confirmationConfigOverride: args.options.confirmationConfig\n });\n}\n\n/**\n * Signs transactions with actions, without broadcasting them\n *\n * @param context - TatchiPasskey context\n * @param nearAccountId - NEAR account ID to sign transactions with\n * @param actionArgs - Action arguments to sign transactions with\n * @param options - Options for the action\n * @returns Promise resolving to the action result\n */\nexport async function signTransactionsWithActions(args: {\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n transactionInputs: TransactionInput[],\n options: SignTransactionHooksOptions,\n}): Promise<SignTransactionResult[]> {\n try {\n const signerMode = mergeSignerMode(getBaseSignerMode(args.context), args.options?.signerMode);\n return signTransactionsWithActionsInternal({\n context: args.context,\n nearAccountId: args.nearAccountId,\n transactionInputs: args.transactionInputs,\n signerMode,\n options: args.options,\n confirmationConfigOverride: args.options.confirmationConfig\n // Public API always uses undefined override (respects user settings)\n });\n } catch (error: unknown) {\n throw toError(error);\n }\n}\n\n/**\n * 3. Transaction Broadcasting - Broadcasts the signed transaction to NEAR network\n * This method broadcasts a previously signed transaction and waits for execution\n *\n * @param context - TatchiPasskey context\n * @param signedTransaction - The signed transaction to broadcast\n * @param waitUntil - The execution status to wait for (defaults to FINAL)\n * @returns Promise resolving to the transaction execution outcome\n *\n * @example\n * ```typescript\n * // Sign a transaction first\n * const signedTransactions = await signTransactionsWithActions(context, 'alice.near', {\n * transactions: [{\n * nearAccountId: 'alice.near',\n * receiverId: 'bob.near',\n * actions: [{\n * action_type: ActionType.Transfer,\n * deposit: '1000000000000000000000000'\n * }],\n * nonce: '123'\n * }]\n * });\n *\n * // Then broadcast it\n * const result = await sendTransaction(\n * context,\n * signedTransactions[0].signedTransaction,\n * TxExecutionStatus.FINAL\n * );\n * ```\n *\n * sendTransaction centrally manages nonce lifecycle around broadcast:\n * - On success: reconciles nonce with chain via updateNonceFromBlockchain() (async),\n * which also prunes any stale reservations.\n * - On failure: releases the reserved nonce immediately to avoid leaks.\n * Callers SHOULD NOT release the nonce in their own catch blocks.\n */\nexport async function sendTransaction({\n context,\n signedTransaction,\n options,\n}: {\n context: PasskeyManagerContext,\n signedTransaction: SignedTransaction,\n options?: SendTransactionHooksOptions,\n}): Promise<ActionResult> {\n\n options?.onEvent?.({\n step: 7,\n phase: ActionPhase.STEP_7_BROADCASTING,\n status: ActionStatus.PROGRESS,\n message: `Broadcasting transaction...`\n });\n\n let transactionResult;\n let txId;\n try {\n // Debug snapshot of the signed transaction shape to aid integration debugging.\n try {\n const st: any = signedTransaction as any;\n const snapshot = {\n type: typeof st,\n keys: st && typeof st === 'object' ? Object.keys(st) : null,\n hasBase64Encode: typeof st?.base64Encode === 'function',\n hasEncode: typeof st?.encode === 'function',\n hasSnakeBytes: !!st?.borsh_bytes,\n hasCamelBytes: !!st?.borshBytes,\n };\n console.debug('[sendTransaction] signedTransaction snapshot', snapshot);\n } catch {\n // best-effort logging only\n }\n\n transactionResult = await context.nearClient.sendTransaction(\n signedTransaction,\n options?.waitUntil\n );\n txId = transactionResult.transaction?.hash || transactionResult.transaction?.id;\n\n // Update nonce from blockchain after successful transaction broadcast asynchronously\n const nonce = signedTransaction.transaction.nonce;\n context.webAuthnManager.getNonceManager().updateNonceFromBlockchain(\n context.nearClient,\n nonce.toString()\n ).catch((error) => {\n console.warn('[sendTransaction] Failed to update nonce from blockchain:', error);\n // don't fail transaction if nonce update fails\n });\n\n options?.onEvent?.({\n step: 7,\n phase: ActionPhase.STEP_7_BROADCASTING,\n status: ActionStatus.SUCCESS,\n message: `Transaction ${txId} sent successfully`\n });\n options?.onEvent?.({\n step: 8,\n phase: ActionPhase.STEP_8_ACTION_COMPLETE,\n status: ActionStatus.SUCCESS,\n message: `Transaction ${txId} completed `\n });\n } catch (error: unknown) {\n const e = toError(error);\n console.error('[sendTransaction] failed:', e);\n const details = (e as { details?: unknown }).details;\n if (details) {\n // Surface full details at error level for visibility during debugging\n console.error('[sendTransaction] RPC error details:', details);\n }\n // Centralized cleanup: release reserved nonce on failure (idempotent)\n try {\n const nonce = signedTransaction.transaction.nonce;\n context.webAuthnManager.getNonceManager().releaseNonce(nonce.toString());\n } catch (nonceError) {\n console.warn('[sendTransaction]: Failed to release nonce after failure:', nonceError);\n }\n throw e;\n }\n\n return {\n success: true,\n transactionId: txId,\n result: transactionResult\n };\n}\n\n//////////////////////////////\n// === INTERNAL API ===\n//////////////////////////////\n\n/**\n * Internal API for executing actions with optional confirmation override\n * @internal - Only used by internal SDK components like SecureTxConfirmButton\n *\n * @param context - TatchiPasskey context\n * @param nearAccountId - NEAR account ID to sign transactions with\n * @param actionArgs - Action arguments to sign transactions with\n * @param options - Options for the action\n * @returns Promise resolving to the action result\n */\nexport async function executeActionInternal({\n context,\n nearAccountId,\n receiverId,\n actionArgs,\n signerMode,\n options,\n confirmationConfigOverride,\n}: {\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n receiverId: AccountId,\n actionArgs: ActionArgs | ActionArgs[],\n signerMode: SignerMode,\n options?: ActionHooksOptions,\n // Accept partial override and merge later in confirm flow\n confirmationConfigOverride?: Partial<ConfirmationConfig> | undefined,\n}): Promise<ActionResult> {\n\n const { onEvent, onError, afterCall, waitUntil } = options || {};\n const confirmerText = options?.confirmerText;\n const actions = Array.isArray(actionArgs) ? actionArgs : [actionArgs];\n\n try {\n // Pre-warm NonceManager with fresh transaction context data without blocking UI feedback\n void context.webAuthnManager\n .getNonceManager()\n .getNonceBlockHashAndHeight(context.nearClient)\n .catch((error) => {\n console.warn('[executeAction]: Failed to pre-warm NonceManager:', error);\n // Continue execution - NonceManager will fall back to direct RPC calls if needed\n });\n\n const signedTxs = await signTransactionsWithActionsInternal({\n context,\n nearAccountId,\n transactionInputs: [{\n receiverId: receiverId,\n actions: actions,\n }],\n signerMode,\n options: { onEvent, onError, waitUntil, confirmerText },\n confirmationConfigOverride\n });\n\n const txResult = await sendTransaction({\n context,\n signedTransaction: signedTxs[0].signedTransaction,\n options: { onEvent, onError, waitUntil }\n });\n afterCall?.(true, txResult);\n return txResult;\n\n } catch (error: unknown) {\n console.error('[executeAction] Error during execution:', error);\n const e = toError(error);\n const short = (e as { short?: string }).short || getNearShortErrorMessage(e);\n onError?.(e);\n onEvent?.({\n step: 0,\n phase: ActionPhase.ACTION_ERROR,\n status: ActionStatus.ERROR,\n message: `Action failed: ${short || e.message}`,\n error: short || e.message\n });\n const result: ActionResult = {\n success: false,\n error: e.message,\n // propagate structured RPC details when present so UIs can render helpful errors\n errorDetails: (e as any)?.details,\n transactionId: undefined,\n };\n afterCall?.(false);\n return result;\n }\n}\n\nexport async function signAndSendTransactionsInternal({\n context,\n nearAccountId,\n transactionInputs,\n signerMode,\n options,\n confirmationConfigOverride,\n}: {\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n transactionInputs: TransactionInput[],\n signerMode: SignerMode,\n options?: SignAndSendTransactionHooksOptions,\n confirmationConfigOverride?: Partial<ConfirmationConfig> | undefined,\n}): Promise<ActionResult[]> {\n\n try {\n const signedTxs = await signTransactionsWithActionsInternal({\n context,\n nearAccountId,\n transactionInputs,\n signerMode,\n options,\n confirmationConfigOverride\n });\n\n const plan = parseExecutionWait(options);\n const txResults: ActionResult[] = [];\n for (let i = 0; i < signedTxs.length; i++) {\n const tx = signedTxs[i];\n const txResult = await sendTransaction({\n context,\n signedTransaction: tx.signedTransaction,\n options: {\n onEvent: options?.onEvent,\n waitUntil: plan.waitUntil ?? options?.waitUntil,\n }\n });\n txResults.push(txResult);\n }\n return txResults;\n } catch (error: unknown) {\n // If signing fails, release all reserved nonces\n context.webAuthnManager.getNonceManager().releaseAllNonces();\n const e = toError(error);\n const short = (e as { short?: string }).short || getNearShortErrorMessage(e) || e.message;\n options?.onEvent?.({\n step: 0,\n phase: ActionPhase.ACTION_ERROR,\n status: ActionStatus.ERROR,\n message: `Action failed: ${short}`,\n error: short\n });\n options?.onError?.(e);\n throw e;\n }\n}\n\n/**\n * Internal API for signing transactions with actions\n * @internal - Only used by internal SDK components with confirmationConfigOverride\n *\n * @param context - TatchiPasskey context\n * @param nearAccountId - NEAR account ID to sign transactions with\n * @param actionArgs - Action arguments to sign transactions with\n * @param options - Options for the action\n * @returns Promise resolving to the action result\n */\nexport async function signTransactionsWithActionsInternal({\n context,\n nearAccountId,\n transactionInputs,\n signerMode,\n options,\n confirmationConfigOverride,\n}: {\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n transactionInputs: TransactionInput[],\n signerMode: SignerMode,\n options?: Omit<ActionHooksOptions, 'afterCall'>,\n confirmationConfigOverride?: Partial<ConfirmationConfig> | undefined,\n}): Promise<SignTransactionResult[]> {\n\n const { onEvent, onError, waitUntil, confirmerText } = options || {};\n\n try {\n // Emit started event\n onEvent?.({\n step: 1,\n phase: ActionPhase.STEP_1_PREPARATION,\n status: ActionStatus.PROGRESS,\n message: transactionInputs.length > 1\n ? `Starting batched transaction with ${transactionInputs.length} actions`\n : `Starting ${transactionInputs[0].actions[0].type} action`\n });\n\n // 1. Basic validation (NEAR data fetching moved to confirmation flow)\n await validateInputsOnly(nearAccountId, transactionInputs, { onEvent, onError, waitUntil });\n\n // 2. VRF Authentication + Transaction Signing (NEAR data fetched in confirmation flow)\n onEvent?.({\n step: 2,\n phase: ActionPhase.STEP_2_USER_CONFIRMATION,\n status: ActionStatus.PROGRESS,\n message: 'Requesting user confirmation...'\n });\n\n // Convert all actions to ActionArgsWasm format for batched transaction\n const transactionInputsWasm: TransactionInputWasm[] = transactionInputs.map(tx => {\n return {\n receiverId: tx.receiverId,\n actions: tx.actions.map(action => toActionArgsWasm(action)),\n }\n });\n\n // VRF challenge and NEAR data will be generated in the confirmation flow\n // - Nonce will be fetched within the confirmation flow\n // This eliminates the ~500ms blocking operations before modal display\n return context.webAuthnManager.signTransactionsWithActions({\n transactions: transactionInputsWasm,\n rpcCall: {\n contractId: context.configs.contractId,\n nearRpcUrl: context.configs.nearRpcUrl,\n nearAccountId: nearAccountId, // caller account\n },\n signerMode,\n // VRF challenge and NEAR data computed in confirmation flow\n confirmationConfigOverride: confirmationConfigOverride,\n title: confirmerText?.title,\n body: confirmerText?.body,\n // Pass through the onEvent callback for progress updates\n onEvent: onEvent ? (progressEvent: onProgressEvents) => {\n if (progressEvent.phase === ActionPhase.STEP_3_WEBAUTHN_AUTHENTICATION) {\n onEvent?.({\n step: 3,\n phase: ActionPhase.STEP_3_WEBAUTHN_AUTHENTICATION,\n status: ActionStatus.PROGRESS,\n message: 'Authenticating with contract...',\n });\n }\n if (progressEvent.phase === ActionPhase.STEP_4_AUTHENTICATION_COMPLETE) {\n onEvent?.({\n step: 4,\n phase: ActionPhase.STEP_4_AUTHENTICATION_COMPLETE,\n status: ActionStatus.SUCCESS,\n message: 'WebAuthn verification complete',\n });\n }\n if (progressEvent.phase === ActionPhase.STEP_5_TRANSACTION_SIGNING_PROGRESS) {\n onEvent?.({\n step: 5,\n phase: ActionPhase.STEP_5_TRANSACTION_SIGNING_PROGRESS,\n status: ActionStatus.PROGRESS,\n message: 'Signing transaction...',\n });\n }\n if (progressEvent.phase === ActionPhase.STEP_6_TRANSACTION_SIGNING_COMPLETE) {\n onEvent?.({\n step: 6,\n phase: ActionPhase.STEP_6_TRANSACTION_SIGNING_COMPLETE,\n status: ActionStatus.SUCCESS,\n message: 'Transaction signed successfully',\n });\n }\n // Bridge worker onProgressEvents (generic) to ActionSSEEvent expected by public hooks\n onEvent(progressEvent as ActionSSEEvent);\n } : undefined,\n });\n\n } catch (error: any) {\n console.error('[signTransactionsWithActions] Error during execution:', error);\n const e = toError(error);\n const short = (e as { short?: string }).short || getNearShortErrorMessage(e) || e.message;\n onError?.(e);\n onEvent?.({\n step: 0,\n phase: ActionPhase.ACTION_ERROR,\n status: ActionStatus.ERROR,\n message: `Action failed: ${short}`,\n error: short\n });\n throw e;\n }\n}\n\nasync function validateInputsOnly(\n nearAccountId: AccountId,\n transactionInputs: TransactionInput[],\n options?: Omit<ActionHooksOptions, 'afterCall'>,\n): Promise<void> {\n const { onEvent, onError } = options || {};\n\n // Basic validation\n if (!nearAccountId) {\n throw new Error('User not logged in or NEAR account ID not set for direct action.');\n }\n\n onEvent?.({\n step: 1,\n phase: ActionPhase.STEP_1_PREPARATION,\n status: ActionStatus.PROGRESS,\n message: 'Validating inputs...'\n });\n\n if (transactionInputs.length === 0) {\n throw new Error('No payloads provided for signing');\n }\n\n for (const transactionInput of transactionInputs) {\n if (!transactionInput.receiverId) {\n throw new Error('Missing required parameter: receiverId');\n }\n for (const action of transactionInput.actions) {\n if (action.type === ActionType.FunctionCall && (!action.methodName || action.args === undefined)) {\n throw new Error('Missing required parameters for function call: methodName or args');\n }\n if (action.type === ActionType.Transfer && !action.amount) {\n throw new Error('Missing required parameter for transfer: amount');\n }\n }\n }\n}\n\nfunction getBaseSignerMode(context: PasskeyManagerContext): SignerMode {\n try {\n return context.webAuthnManager.getUserPreferences().getSignerMode();\n } catch {\n return context.configs.signerMode;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"actions.js","names":["mergeSignerMode","error: unknown","toError","ActionPhase","ActionStatus","st: any","getNearShortErrorMessage","result: ActionResult","txResults: ActionResult[]","transactionInputsWasm: TransactionInputWasm[]","toActionArgsWasm","error: any","ActionType"],"sources":["../../../../src/core/TatchiPasskey/actions.ts"],"sourcesContent":["import { ActionType, toActionArgsWasm } from '../types/actions';\nimport type {\n ActionHooksOptions,\n ExecutionWaitOption,\n SendTransactionHooksOptions,\n SignAndSendTransactionHooksOptions,\n SignTransactionHooksOptions,\n} from '../types/sdkSentEvents';\nimport type { ActionResult, SignTransactionResult } from '../types/tatchi';\nimport type { TxExecutionStatus } from '@near-js/types';\nimport type { ActionArgs, TransactionInput, TransactionInputWasm } from '../types/actions';\nimport { type ConfirmationConfig, type SignerMode, mergeSignerMode } from '../types/signer-worker';\nimport type { PasskeyManagerContext } from './index';\nimport type { SignedTransaction } from '../NearClient';\nimport type { AccountId } from '../types/accountIds';\nimport { ActionPhase, ActionStatus, type ActionSSEEvent, type onProgressEvents } from '../types/sdkSentEvents';\nimport { toError, getNearShortErrorMessage } from '../../utils/errors';\n\n/**\n * executeAction signs a single transaction (with actions[]) to a single receiver.\n * If you want to sign multiple transactions to different receivers,\n * use signTransactionsWithActions() instead.\n *\n * @param context - TatchiPasskey context\n * @param nearAccountId - NEAR account ID to sign transactions with\n * @param actionArgs - Action arguments to sign transactions with\n * @param options - Options for the action\n * @returns Promise resolving to the action result\n */\nexport async function executeAction(args: {\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n receiverId: AccountId,\n actionArgs: ActionArgs | ActionArgs[],\n options: ActionHooksOptions,\n}): Promise<ActionResult> {\n try {\n const signerMode = mergeSignerMode(getBaseSignerMode(args.context), args.options?.signerMode);\n // Thread optional per-call confirmation override when provided; otherwise\n // user preferences determine the confirmation behavior.\n return executeActionInternal({\n context: args.context,\n nearAccountId: args.nearAccountId,\n receiverId: args.receiverId,\n actionArgs: args.actionArgs,\n signerMode,\n options: args.options,\n confirmationConfigOverride: args.options.confirmationConfig\n });\n } catch (error: unknown) {\n throw toError(error);\n }\n}\n\n// Execution plan types for broadcasting multiple transactions\n// Helper: parse executionWait (only sequential for now)\nfunction parseExecutionWait(options?: SignAndSendTransactionHooksOptions): { waitUntil?: TxExecutionStatus } {\n return { waitUntil: options?.waitUntil };\n}\n\n/**\n * Signs multiple transactions with actions, and broadcasts them\n *\n * @param context - TatchiPasskey context\n * @param nearAccountId - NEAR account ID to sign transactions with\n * @param transactionInput - Transaction input to sign\n * @param options - Options for the action\n * @returns Promise resolving to the action result\n */\nexport async function signAndSendTransactions(args: {\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n transactionInputs: TransactionInput[],\n options: SignAndSendTransactionHooksOptions,\n}): Promise<ActionResult[]> {\n const signerMode = mergeSignerMode(getBaseSignerMode(args.context), args.options?.signerMode);\n return signAndSendTransactionsInternal({\n context: args.context,\n nearAccountId: args.nearAccountId,\n transactionInputs: args.transactionInputs,\n signerMode,\n options: args.options,\n confirmationConfigOverride: args.options.confirmationConfig\n });\n}\n\n/**\n * Signs transactions with actions, without broadcasting them\n *\n * @param context - TatchiPasskey context\n * @param nearAccountId - NEAR account ID to sign transactions with\n * @param actionArgs - Action arguments to sign transactions with\n * @param options - Options for the action\n * @returns Promise resolving to the action result\n */\nexport async function signTransactionsWithActions(args: {\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n transactionInputs: TransactionInput[],\n options: SignTransactionHooksOptions,\n}): Promise<SignTransactionResult[]> {\n try {\n const signerMode = mergeSignerMode(getBaseSignerMode(args.context), args.options?.signerMode);\n return signTransactionsWithActionsInternal({\n context: args.context,\n nearAccountId: args.nearAccountId,\n transactionInputs: args.transactionInputs,\n signerMode,\n options: args.options,\n confirmationConfigOverride: args.options.confirmationConfig\n // Public API always uses undefined override (respects user settings)\n });\n } catch (error: unknown) {\n throw toError(error);\n }\n}\n\n/**\n * 3. Transaction Broadcasting - Broadcasts the signed transaction to NEAR network\n * This method broadcasts a previously signed transaction and waits for execution\n *\n * @param context - TatchiPasskey context\n * @param signedTransaction - The signed transaction to broadcast\n * @param waitUntil - The execution status to wait for (defaults to FINAL)\n * @returns Promise resolving to the transaction execution outcome\n *\n * @example\n * ```typescript\n * // Sign a transaction first\n * const signedTransactions = await signTransactionsWithActions(context, 'alice.near', {\n * transactions: [{\n * nearAccountId: 'alice.near',\n * receiverId: 'bob.near',\n * actions: [{\n * action_type: ActionType.Transfer,\n * deposit: '1000000000000000000000000'\n * }],\n * nonce: '123'\n * }]\n * });\n *\n * // Then broadcast it\n * const result = await sendTransaction(\n * context,\n * signedTransactions[0].signedTransaction,\n * TxExecutionStatus.FINAL\n * );\n * ```\n *\n * sendTransaction centrally manages nonce lifecycle around broadcast:\n * - On success: reconciles nonce with chain via updateNonceFromBlockchain() (async),\n * which also prunes any stale reservations.\n * - On failure: releases the reserved nonce immediately to avoid leaks.\n * Callers SHOULD NOT release the nonce in their own catch blocks.\n */\nexport async function sendTransaction({\n context,\n signedTransaction,\n options,\n}: {\n context: PasskeyManagerContext,\n signedTransaction: SignedTransaction,\n options?: SendTransactionHooksOptions,\n}): Promise<ActionResult> {\n\n options?.onEvent?.({\n step: 7,\n phase: ActionPhase.STEP_7_BROADCASTING,\n status: ActionStatus.PROGRESS,\n message: `Broadcasting transaction...`\n });\n\n let transactionResult;\n let txId;\n try {\n // Debug snapshot of the signed transaction shape to aid integration debugging.\n try {\n const st: any = signedTransaction as any;\n const snapshot = {\n type: typeof st,\n keys: st && typeof st === 'object' ? Object.keys(st) : null,\n hasBase64Encode: typeof st?.base64Encode === 'function',\n hasEncode: typeof st?.encode === 'function',\n hasSnakeBytes: !!st?.borsh_bytes,\n hasCamelBytes: !!st?.borshBytes,\n };\n console.debug('[sendTransaction] signedTransaction snapshot', snapshot);\n } catch {\n // best-effort logging only\n }\n\n transactionResult = await context.nearClient.sendTransaction(\n signedTransaction,\n options?.waitUntil\n );\n txId = transactionResult.transaction?.hash || transactionResult.transaction?.id;\n\n // Update nonce from blockchain after successful transaction broadcast asynchronously\n const nonce = signedTransaction.transaction.nonce;\n context.webAuthnManager.getNonceManager().updateNonceFromBlockchain(\n context.nearClient,\n nonce.toString()\n ).catch((error) => {\n console.warn('[sendTransaction] Failed to update nonce from blockchain:', error);\n // don't fail transaction if nonce update fails\n });\n\n options?.onEvent?.({\n step: 7,\n phase: ActionPhase.STEP_7_BROADCASTING,\n status: ActionStatus.SUCCESS,\n message: `Transaction ${txId} sent successfully`\n });\n options?.onEvent?.({\n step: 8,\n phase: ActionPhase.STEP_8_ACTION_COMPLETE,\n status: ActionStatus.SUCCESS,\n message: `Transaction ${txId} completed `\n });\n } catch (error: unknown) {\n const e = toError(error);\n console.error('[sendTransaction] failed:', e);\n const details = (e as { details?: unknown }).details;\n if (details) {\n // Surface full details at error level for visibility during debugging\n console.error('[sendTransaction] RPC error details:', details);\n }\n // Centralized cleanup: release reserved nonce on failure (idempotent)\n try {\n const nonce = signedTransaction.transaction.nonce;\n context.webAuthnManager.getNonceManager().releaseNonce(nonce.toString());\n } catch (nonceError) {\n console.warn('[sendTransaction]: Failed to release nonce after failure:', nonceError);\n }\n throw e;\n }\n\n return {\n success: true,\n transactionId: txId,\n result: transactionResult\n };\n}\n\n//////////////////////////////\n// === INTERNAL API ===\n//////////////////////////////\n\n/**\n * Internal API for executing actions with optional confirmation override\n * @internal - Only used by internal SDK components like SecureTxConfirmButton\n *\n * @param context - TatchiPasskey context\n * @param nearAccountId - NEAR account ID to sign transactions with\n * @param actionArgs - Action arguments to sign transactions with\n * @param options - Options for the action\n * @returns Promise resolving to the action result\n */\nexport async function executeActionInternal({\n context,\n nearAccountId,\n receiverId,\n actionArgs,\n signerMode,\n options,\n confirmationConfigOverride,\n}: {\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n receiverId: AccountId,\n actionArgs: ActionArgs | ActionArgs[],\n signerMode: SignerMode,\n options?: ActionHooksOptions,\n // Accept partial override and merge later in confirm flow\n confirmationConfigOverride?: Partial<ConfirmationConfig> | undefined,\n}): Promise<ActionResult> {\n\n const { onEvent, onError, afterCall, waitUntil } = options || {};\n const confirmerText = options?.confirmerText;\n const actions = Array.isArray(actionArgs) ? actionArgs : [actionArgs];\n\n try {\n // Pre-warm NonceManager with fresh transaction context data without blocking UI feedback\n void context.webAuthnManager\n .getNonceManager()\n .getNonceBlockHashAndHeight(context.nearClient)\n .catch((error) => {\n console.warn('[executeAction]: Failed to pre-warm NonceManager:', error);\n // Continue execution - NonceManager will fall back to direct RPC calls if needed\n });\n\n const signedTxs = await signTransactionsWithActionsInternal({\n context,\n nearAccountId,\n transactionInputs: [{\n receiverId: receiverId,\n actions: actions,\n }],\n signerMode,\n options: { onEvent, onError, waitUntil, confirmerText },\n confirmationConfigOverride\n });\n\n const txResult = await sendTransaction({\n context,\n signedTransaction: signedTxs[0].signedTransaction,\n options: { onEvent, onError, waitUntil }\n });\n afterCall?.(true, txResult);\n return txResult;\n\n } catch (error: unknown) {\n console.error('[executeAction] Error during execution:', error);\n const e = toError(error);\n const short = (e as { short?: string }).short || getNearShortErrorMessage(e);\n onError?.(e);\n onEvent?.({\n step: 0,\n phase: ActionPhase.ACTION_ERROR,\n status: ActionStatus.ERROR,\n message: `Action failed: ${short || e.message}`,\n error: short || e.message\n });\n const result: ActionResult = {\n success: false,\n error: e.message,\n // propagate structured RPC details when present so UIs can render helpful errors\n errorDetails: (e as any)?.details,\n transactionId: undefined,\n };\n afterCall?.(false);\n return result;\n }\n}\n\nexport async function signAndSendTransactionsInternal({\n context,\n nearAccountId,\n transactionInputs,\n signerMode,\n options,\n confirmationConfigOverride,\n}: {\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n transactionInputs: TransactionInput[],\n signerMode: SignerMode,\n options?: SignAndSendTransactionHooksOptions,\n confirmationConfigOverride?: Partial<ConfirmationConfig> | undefined,\n}): Promise<ActionResult[]> {\n\n try {\n const signedTxs = await signTransactionsWithActionsInternal({\n context,\n nearAccountId,\n transactionInputs,\n signerMode,\n options,\n confirmationConfigOverride\n });\n\n const plan = parseExecutionWait(options);\n const txResults: ActionResult[] = [];\n for (let i = 0; i < signedTxs.length; i++) {\n const tx = signedTxs[i];\n const txResult = await sendTransaction({\n context,\n signedTransaction: tx.signedTransaction,\n options: {\n onEvent: options?.onEvent,\n waitUntil: plan.waitUntil ?? options?.waitUntil,\n }\n });\n txResults.push(txResult);\n }\n return txResults;\n } catch (error: unknown) {\n // If signing fails, release all reserved nonces\n context.webAuthnManager.getNonceManager().releaseAllNonces();\n const e = toError(error);\n const short = (e as { short?: string }).short || getNearShortErrorMessage(e) || e.message;\n options?.onEvent?.({\n step: 0,\n phase: ActionPhase.ACTION_ERROR,\n status: ActionStatus.ERROR,\n message: `Action failed: ${short}`,\n error: short\n });\n options?.onError?.(e);\n throw e;\n }\n}\n\n/**\n * Internal API for signing transactions with actions\n * @internal - Only used by internal SDK components with confirmationConfigOverride\n *\n * @param context - TatchiPasskey context\n * @param nearAccountId - NEAR account ID to sign transactions with\n * @param actionArgs - Action arguments to sign transactions with\n * @param options - Options for the action\n * @returns Promise resolving to the action result\n */\nexport async function signTransactionsWithActionsInternal({\n context,\n nearAccountId,\n transactionInputs,\n signerMode,\n options,\n confirmationConfigOverride,\n}: {\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n transactionInputs: TransactionInput[],\n signerMode: SignerMode,\n options?: Omit<ActionHooksOptions, 'afterCall'>,\n confirmationConfigOverride?: Partial<ConfirmationConfig> | undefined,\n}): Promise<SignTransactionResult[]> {\n\n const { onEvent, onError, waitUntil, confirmerText } = options || {};\n\n try {\n // Emit started event\n onEvent?.({\n step: 1,\n phase: ActionPhase.STEP_1_PREPARATION,\n status: ActionStatus.PROGRESS,\n message: transactionInputs.length > 1\n ? `Starting batched transaction with ${transactionInputs.length} actions`\n : `Starting ${transactionInputs[0].actions[0].type} action`\n });\n\n // 1. Basic validation (NEAR data fetching moved to confirmation flow)\n await validateInputsOnly(nearAccountId, transactionInputs, { onEvent, onError, waitUntil });\n\n // 2. VRF Authentication + Transaction Signing (NEAR data fetched in confirmation flow)\n onEvent?.({\n step: 2,\n phase: ActionPhase.STEP_2_USER_CONFIRMATION,\n status: ActionStatus.PROGRESS,\n message: 'Requesting user confirmation...'\n });\n\n // Convert all actions to ActionArgsWasm format for batched transaction\n const transactionInputsWasm: TransactionInputWasm[] = transactionInputs.map(tx => {\n return {\n receiverId: tx.receiverId,\n actions: tx.actions.map(action => toActionArgsWasm(action)),\n }\n });\n\n // VRF challenge and NEAR data will be generated in the confirmation flow\n // - Nonce will be fetched within the confirmation flow\n // This eliminates the ~500ms blocking operations before modal display\n return context.webAuthnManager.signTransactionsWithActions({\n transactions: transactionInputsWasm,\n rpcCall: {\n contractId: context.configs.contractId,\n nearRpcUrl: context.configs.nearRpcUrl,\n nearAccountId: nearAccountId, // caller account\n },\n signerMode,\n // VRF challenge and NEAR data computed in confirmation flow\n confirmationConfigOverride: confirmationConfigOverride,\n title: confirmerText?.title,\n body: confirmerText?.body,\n // Pass through the onEvent callback for progress updates\n onEvent: onEvent ? (progressEvent: onProgressEvents) => {\n if (progressEvent.phase === ActionPhase.STEP_3_WEBAUTHN_AUTHENTICATION) {\n onEvent?.({\n step: 3,\n phase: ActionPhase.STEP_3_WEBAUTHN_AUTHENTICATION,\n status: ActionStatus.PROGRESS,\n message: 'Authenticating with contract...',\n });\n }\n if (progressEvent.phase === ActionPhase.STEP_4_AUTHENTICATION_COMPLETE) {\n onEvent?.({\n step: 4,\n phase: ActionPhase.STEP_4_AUTHENTICATION_COMPLETE,\n status: ActionStatus.SUCCESS,\n message: 'WebAuthn verification complete',\n });\n }\n if (progressEvent.phase === ActionPhase.STEP_5_TRANSACTION_SIGNING_PROGRESS) {\n onEvent?.({\n step: 5,\n phase: ActionPhase.STEP_5_TRANSACTION_SIGNING_PROGRESS,\n status: ActionStatus.PROGRESS,\n message: 'Signing transaction...',\n });\n }\n if (progressEvent.phase === ActionPhase.STEP_6_TRANSACTION_SIGNING_COMPLETE) {\n // `ActionSSEEvent` reserves STEP_6 for successful signing only.\n // When the worker reports an error status here, we rely on the thrown error\n // to emit `ActionPhase.ACTION_ERROR` in the outer catch.\n if (progressEvent.status !== ActionStatus.ERROR) {\n onEvent?.({\n step: 6,\n phase: ActionPhase.STEP_6_TRANSACTION_SIGNING_COMPLETE,\n status: ActionStatus.SUCCESS,\n message: progressEvent.message || 'Transaction signed successfully',\n });\n }\n }\n // Bridge worker onProgressEvents (generic) to ActionSSEEvent expected by public hooks\n onEvent(progressEvent as ActionSSEEvent);\n } : undefined,\n });\n\n } catch (error: any) {\n console.error('[signTransactionsWithActions] Error during execution:', error);\n const e = toError(error);\n const short = (e as { short?: string }).short || getNearShortErrorMessage(e) || e.message;\n onError?.(e);\n onEvent?.({\n step: 0,\n phase: ActionPhase.ACTION_ERROR,\n status: ActionStatus.ERROR,\n message: `Action failed: ${short}`,\n error: short\n });\n throw e;\n }\n}\n\nasync function validateInputsOnly(\n nearAccountId: AccountId,\n transactionInputs: TransactionInput[],\n options?: Omit<ActionHooksOptions, 'afterCall'>,\n): Promise<void> {\n const { onEvent, onError } = options || {};\n\n // Basic validation\n if (!nearAccountId) {\n throw new Error('User not logged in or NEAR account ID not set for direct action.');\n }\n\n onEvent?.({\n step: 1,\n phase: ActionPhase.STEP_1_PREPARATION,\n status: ActionStatus.PROGRESS,\n message: 'Validating inputs...'\n });\n\n if (transactionInputs.length === 0) {\n throw new Error('No payloads provided for signing');\n }\n\n for (const transactionInput of transactionInputs) {\n if (!transactionInput.receiverId) {\n throw new Error('Missing required parameter: receiverId');\n }\n for (const action of transactionInput.actions) {\n if (action.type === ActionType.FunctionCall && (!action.methodName || action.args === undefined)) {\n throw new Error('Missing required parameters for function call: methodName or args');\n }\n if (action.type === ActionType.Transfer && !action.amount) {\n throw new Error('Missing required parameter for transfer: amount');\n }\n }\n }\n}\n\nfunction getBaseSignerMode(context: PasskeyManagerContext): SignerMode {\n try {\n return context.webAuthnManager.getUserPreferences().getSignerMode();\n } catch {\n return context.configs.signerMode;\n }\n}\n"],"mappings":"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"}
@@ -176,6 +176,10 @@ async function finalizeLoginError(args) {
176
176
  async function prepareThresholdSessionPlan(args) {
177
177
  const { context, nearAccountId, preferredDeviceNumber, relayUrl, ttlMs, remainingUses, wantsThresholdSession } = args;
178
178
  if (!wantsThresholdSession || !relayUrl) return null;
179
+ if (!Number.isFinite(ttlMs) || ttlMs <= 0 || !Number.isFinite(remainingUses) || remainingUses <= 0) {
180
+ console.debug("[login] threshold session mint disabled (ttlMs/remainingUses must be > 0); skipping threshold session");
181
+ return null;
182
+ }
179
183
  const { webAuthnManager } = context;
180
184
  try {
181
185
  const rpId = webAuthnManager.getRpId();
@@ -732,7 +736,13 @@ async function getRecentLogins(context) {
732
736
  */
733
737
  async function logoutAndClearSession(context) {
734
738
  const { webAuthnManager } = context;
735
- await webAuthnManager.clearVrfSession();
739
+ try {
740
+ const clear = webAuthnManager.clearVrfSession().catch(() => void 0);
741
+ await Promise.race([clear, new Promise((resolve) => setTimeout(resolve, 2e3))]);
742
+ } catch {}
743
+ try {
744
+ webAuthnManager.resetSigningState();
745
+ } catch {}
736
746
  try {
737
747
  webAuthnManager.getNonceManager().clear();
738
748
  } catch {}
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","names":["LoginPhase","LoginStatus","parseDeviceNumber","err: any","getUserFriendlyErrorMessage","signingSession: SigningSessionStatus","IndexedDBManager","normalizeThresholdEd25519ParticipantIds","buildThresholdSessionPolicy","makeThresholdEd25519AuthSessionCacheKey","e: any","clientVerifyingShareB64u: string | null","mintThresholdEd25519AuthSession","computeLoginIntentDigest","authenticatorsToAllowCredentials","serverSessionJwt: string | undefined","verifyAuthenticationResponse","loginResult: LoginResult","createRandomVRFChallenge","hintUserPromise: Promise<ClientUserData | null>","userData: ClientUserData | null","unlockResult: { success: boolean; error?: string }","unlockCredential: WebAuthnAuthenticationCredential | undefined","error: any","relayerUrl","refreshErr: any","result: LoginResult"],"sources":["../../../../src/core/TatchiPasskey/login.ts"],"sourcesContent":["import type {\n AfterCall,\n LoginHooksOptions,\n LoginSSEvent,\n} from '../types/sdkSentEvents';\nimport { LoginPhase, LoginStatus } from '../types/sdkSentEvents';\nimport type {\n GetRecentLoginsResult,\n LoginAndCreateSessionResult,\n LoginResult,\n LoginSession,\n LoginState,\n SigningSessionStatus,\n} from '../types/tatchi';\nimport type { PasskeyManagerContext } from './index';\nimport type { AccountId } from '../types/accountIds';\nimport type { WebAuthnAuthenticationCredential } from '../types/webauthn';\nimport { getUserFriendlyErrorMessage } from '../../utils/errors';\nimport { createRandomVRFChallenge, VRFChallenge } from '../types/vrf-worker';\nimport { authenticatorsToAllowCredentials } from '../WebAuthnManager/touchIdPrompt';\nimport { IndexedDBManager } from '../IndexedDBManager';\nimport type { ClientAuthenticatorData, ClientUserData } from '../IndexedDBManager';\nimport { verifyAuthenticationResponse } from '../rpcCalls';\nimport { computeLoginIntentDigest } from '../digests/intentDigest';\nimport { buildThresholdSessionPolicy } from '../threshold/thresholdSessionPolicy';\nimport { parseDeviceNumber } from '../WebAuthnManager/SignerWorkerManager/getDeviceNumber';\nimport {\n clearAllCachedThresholdEd25519AuthSessions,\n makeThresholdEd25519AuthSessionCacheKey,\n mintThresholdEd25519AuthSession,\n putCachedThresholdEd25519AuthSession,\n} from '../threshold/thresholdEd25519AuthSession';\nimport { normalizeThresholdEd25519ParticipantIds } from '../../threshold/participants';\n\ntype WarmSigningSessionPolicy = { ttlMs: number; remainingUses: number };\n\ntype ThresholdSessionPlan = {\n sessionKind: 'jwt';\n relayerUrl: string;\n relayerKeyId: string;\n cacheKey: string;\n policy: Awaited<ReturnType<typeof buildThresholdSessionPolicy>>;\n deviceNumber: number;\n};\n\n/**\n * Core login function that handles passkey authentication without React dependencies.\n *\n * - Unlocks the VRF keypair (Shamir 3‑pass auto‑unlock when possible; falls back to TouchID).\n * - Updates local login state and returns success with account/public key info.\n * - Optional: mints a server session when `options.session` is provided.\n * - Generates a fresh, chain‑anchored VRF challenge (using latest block).\n * - Collects a WebAuthn assertion over the VRF output and posts to the relay route\n * (defaults to `/verify-authentication-response`).\n * - When `kind: 'jwt'`, returns the token in `result.jwt`.\n * - When `kind: 'cookie'`, the server sets an HttpOnly cookie and no JWT is returned.\n */\nexport async function loginAndCreateSession(\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n options?: LoginHooksOptions\n): Promise<LoginAndCreateSessionResult> {\n const { onEvent, onError, afterCall } = options || {};\n const { webAuthnManager } = context;\n\n onEvent?.({\n step: 1,\n phase: LoginPhase.STEP_1_PREPARATION,\n status: LoginStatus.PROGRESS,\n message: `Starting login for ${nearAccountId}`\n });\n\n const prevStatus = await webAuthnManager.checkVrfStatus();\n const prevVrfAccountId = prevStatus?.active ? prevStatus.nearAccountId : null;\n\n // If this call activates VRF then fails, clear the partial session.\n const rollbackVrfOnFailure = async () => {\n const status = await webAuthnManager.checkVrfStatus();\n const current = status?.active ? status.nearAccountId : null;\n if (current && current !== prevVrfAccountId) {\n await logoutAndClearSession(context);\n }\n };\n\n try {\n // Validation\n if (!window.isSecureContext) {\n const errorMessage = 'Passkey operations require a secure context (HTTPS or localhost).';\n return await finalizeLoginError({\n message: errorMessage,\n error: new Error(errorMessage),\n rollbackVrfOnFailure,\n onEvent,\n onError,\n afterCall,\n callAfterCall: false,\n });\n }\n\n const session = options?.session;\n const wantsServerSession = session !== undefined;\n const deviceNumberHint = parseDeviceNumber(options?.deviceNumber, { min: 1 });\n const base = await handleLoginUnlockVRF(\n context,\n nearAccountId,\n onEvent,\n onError,\n afterCall,\n // Defer final 'login-complete' event & afterCall until warm signing session is minted\n true,\n deviceNumberHint\n );\n // If base login failed, just return\n if (!base.result.success) {\n return base.result;\n }\n\n const preferredDeviceNumber = deviceNumberHint ?? base.activeDeviceNumber;\n const warmPolicy = resolveWarmSigningSessionPolicy(context, options);\n\n const wantsThresholdSession =\n webAuthnManager.getUserPreferences().getSignerMode().mode === 'threshold-signer';\n const relayUrl = (session?.relayUrl || context.configs.relayer.url).trim();\n const verifyRoute = (session?.route || '/verify-authentication-response').trim();\n\n const thresholdPlan = await prepareThresholdSessionPlan({\n context,\n nearAccountId,\n preferredDeviceNumber,\n relayUrl,\n ttlMs: warmPolicy.ttlMs,\n remainingUses: warmPolicy.remainingUses,\n wantsThresholdSession,\n });\n\n const wantsRelayerSession = wantsServerSession || thresholdPlan !== null;\n if (wantsRelayerSession && !relayUrl) {\n console.warn('[login] No relayUrl provided for session-style signing');\n }\n\n if (wantsRelayerSession && relayUrl) {\n return await runRelayerSessionFlow({\n context,\n nearAccountId,\n baseLoginResult: base.result,\n baseUnlockCredential: base.unlockCredential,\n preferredDeviceNumber,\n session,\n relayUrl,\n verifyRoute,\n thresholdPlan,\n warmPolicy,\n rollbackVrfOnFailure,\n onEvent,\n onError,\n afterCall,\n });\n }\n\n // No relayer session requested (or relayUrl missing): mint/refresh warm signing session only.\n await mintWarmSigningSession({\n context,\n nearAccountId,\n onEvent,\n credential: base.unlockCredential,\n ttlMs: warmPolicy.ttlMs,\n remainingUses: warmPolicy.remainingUses,\n });\n return await finalizeLoginSuccess({\n webAuthnManager,\n nearAccountId,\n loginResult: base.result,\n onEvent,\n afterCall,\n });\n } catch (err: any) {\n const errorMessage =\n getUserFriendlyErrorMessage(err, 'login') || err?.message || 'Login failed';\n return await finalizeLoginError({\n message: errorMessage,\n error: err,\n rollbackVrfOnFailure,\n onEvent,\n onError,\n afterCall,\n });\n }\n}\n\nfunction resolveWarmSigningSessionPolicy(\n context: PasskeyManagerContext,\n options?: LoginHooksOptions\n): WarmSigningSessionPolicy {\n const defaults = context.configs.signingSessionDefaults;\n return {\n ttlMs: options?.signingSession?.ttlMs ?? defaults.ttlMs,\n remainingUses: options?.signingSession?.remainingUses ?? defaults.remainingUses,\n };\n}\n\nasync function attachSigningSessionStatus(args: {\n webAuthnManager: PasskeyManagerContext['webAuthnManager'];\n nearAccountId: AccountId;\n loginResult: LoginResult;\n}): Promise<LoginAndCreateSessionResult> {\n const { webAuthnManager, nearAccountId, loginResult } = args;\n if (!loginResult.success) return loginResult;\n try {\n const signingSession: SigningSessionStatus =\n await webAuthnManager.getWarmSigningSessionStatus(nearAccountId);\n return { ...loginResult, signingSession };\n } catch {\n return loginResult;\n }\n}\n\nasync function finalizeLoginSuccess(args: {\n webAuthnManager: PasskeyManagerContext['webAuthnManager'];\n nearAccountId: AccountId;\n loginResult: LoginResult;\n onEvent?: (event: LoginSSEvent) => void;\n afterCall?: AfterCall<LoginAndCreateSessionResult>;\n}): Promise<LoginAndCreateSessionResult> {\n const { webAuthnManager, nearAccountId, loginResult, onEvent, afterCall } = args;\n const finalResult = await attachSigningSessionStatus({\n webAuthnManager,\n nearAccountId,\n loginResult,\n });\n onEvent?.({\n step: 4,\n phase: LoginPhase.STEP_4_LOGIN_COMPLETE,\n status: LoginStatus.SUCCESS,\n message: 'Login completed successfully',\n nearAccountId,\n clientNearPublicKey: loginResult.clientNearPublicKey ?? '',\n });\n await afterCall?.(true, finalResult);\n return finalResult;\n}\n\nasync function finalizeLoginError(args: {\n message: string;\n error?: unknown;\n rollbackVrfOnFailure: () => Promise<void>;\n onEvent?: (event: LoginSSEvent) => void;\n onError?: (error: Error) => void;\n afterCall?: AfterCall<LoginAndCreateSessionResult>;\n callOnError?: boolean;\n callAfterCall?: boolean;\n}): Promise<LoginAndCreateSessionResult> {\n const {\n message,\n error,\n rollbackVrfOnFailure,\n onEvent,\n onError,\n afterCall,\n callOnError = true,\n callAfterCall = true,\n } = args;\n\n try { await rollbackVrfOnFailure(); } catch {}\n\n if (callOnError) {\n onError?.(error as any);\n }\n\n onEvent?.({\n step: 0,\n phase: LoginPhase.LOGIN_ERROR,\n status: LoginStatus.ERROR,\n message,\n error: message,\n });\n\n if (callAfterCall) {\n await afterCall?.(false);\n }\n return { success: false, error: message };\n}\n\nasync function prepareThresholdSessionPlan(args: {\n context: PasskeyManagerContext;\n nearAccountId: AccountId;\n preferredDeviceNumber: number | null;\n relayUrl: string;\n ttlMs: number;\n remainingUses: number;\n wantsThresholdSession: boolean;\n}): Promise<ThresholdSessionPlan | null> {\n const {\n context,\n nearAccountId,\n preferredDeviceNumber,\n relayUrl,\n ttlMs,\n remainingUses,\n wantsThresholdSession,\n } = args;\n if (!wantsThresholdSession || !relayUrl) return null;\n\n const { webAuthnManager } = context;\n\n try {\n const rpId = webAuthnManager.getRpId();\n if (!rpId) throw new Error('Missing rpId for threshold session');\n\n const lastUser = await webAuthnManager.getLastUser();\n const deviceNumber = preferredDeviceNumber ??\n (lastUser?.nearAccountId === nearAccountId\n ? lastUser.deviceNumber\n : (await IndexedDBManager.clientDB.getLastDBUpdatedUser(nearAccountId))\n ?.deviceNumber ?? null);\n\n if (deviceNumber === null) {\n console.warn('[login] threshold-signer configured but no threshold key material found; skipping threshold session');\n return null;\n }\n\n const thresholdKeyMaterial = await IndexedDBManager.nearKeysDB.getThresholdKeyMaterial(\n nearAccountId,\n deviceNumber\n );\n const relayerKeyId = thresholdKeyMaterial?.relayerKeyId || null;\n const participantIds = thresholdKeyMaterial?.participants?.map((p) => p.id) || null;\n const normalizedParticipantIds = normalizeThresholdEd25519ParticipantIds(participantIds);\n\n if (!relayerKeyId) {\n console.warn('[login] threshold-signer configured but no threshold key material found; skipping threshold session');\n return null;\n }\n\n if (!normalizedParticipantIds || normalizedParticipantIds.length < 2) {\n console.warn('[login] threshold key material missing/invalid participantIds; skipping threshold session mint');\n return null;\n }\n\n const policy = await buildThresholdSessionPolicy({\n nearAccountId,\n rpId,\n relayerKeyId,\n ...(normalizedParticipantIds?.length ? { participantIds: normalizedParticipantIds } : {}),\n ttlMs,\n remainingUses,\n });\n\n const cacheKey = makeThresholdEd25519AuthSessionCacheKey({\n nearAccountId,\n rpId,\n relayerUrl: relayUrl,\n relayerKeyId,\n ...(normalizedParticipantIds?.length ? { participantIds: normalizedParticipantIds } : {}),\n });\n\n return {\n sessionKind: 'jwt',\n relayerUrl: relayUrl,\n relayerKeyId,\n cacheKey,\n policy,\n deviceNumber,\n };\n } catch (e: any) {\n console.warn(\n '[login] failed to prepare threshold session policy; skipping threshold session mint:',\n e?.message || e\n );\n return null;\n }\n}\n\nasync function mintThresholdSessionBestEffort(args: {\n context: PasskeyManagerContext;\n nearAccountId: AccountId;\n plan: ThresholdSessionPlan;\n vrfChallenge: VRFChallenge;\n credential: WebAuthnAuthenticationCredential;\n}): Promise<void> {\n const { context, nearAccountId, plan, vrfChallenge, credential } = args;\n const { webAuthnManager } = context;\n\n let clientVerifyingShareB64u: string | null = null;\n try {\n const localKeyMaterial = await IndexedDBManager.nearKeysDB.getLocalKeyMaterial(\n nearAccountId,\n plan.deviceNumber\n );\n const wrapKeySalt = String(localKeyMaterial?.wrapKeySalt || '').trim();\n if (wrapKeySalt) {\n const derived =\n await webAuthnManager.deriveThresholdEd25519ClientVerifyingShareFromCredential({\n credential,\n nearAccountId,\n wrapKeySalt,\n });\n if (derived.success && derived.clientVerifyingShareB64u) {\n clientVerifyingShareB64u = derived.clientVerifyingShareB64u;\n }\n }\n } catch (e: any) {\n console.warn(\n '[login] failed to derive clientVerifyingShareB64u for threshold session mint:',\n e?.message || e\n );\n }\n\n if (!clientVerifyingShareB64u) {\n console.warn(\n '[login] threshold session mint skipped: missing clientVerifyingShareB64u'\n );\n return;\n }\n\n const minted = await mintThresholdEd25519AuthSession({\n relayerUrl: plan.relayerUrl,\n sessionKind: plan.sessionKind,\n relayerKeyId: plan.relayerKeyId,\n clientVerifyingShareB64u,\n sessionPolicy: plan.policy.policy,\n vrfChallenge,\n webauthnAuthentication: credential,\n });\n\n if (minted.ok && minted.jwt) {\n putCachedThresholdEd25519AuthSession(plan.cacheKey, {\n sessionKind: plan.sessionKind,\n policy: plan.policy.policy,\n policyJson: plan.policy.policyJson,\n sessionPolicyDigest32: plan.policy.sessionPolicyDigest32,\n jwt: minted.jwt,\n ...(minted.expiresAtMs ? { expiresAtMs: minted.expiresAtMs } : {}),\n });\n return;\n }\n\n if (!minted.ok) {\n console.warn(\n '[login] threshold session mint failed:',\n minted.code || minted.message || 'unknown error'\n );\n }\n}\n\nasync function runRelayerSessionFlow(args: {\n context: PasskeyManagerContext;\n nearAccountId: AccountId;\n baseLoginResult: LoginResult;\n baseUnlockCredential?: WebAuthnAuthenticationCredential;\n preferredDeviceNumber: number | null;\n session: LoginHooksOptions['session'] | undefined;\n relayUrl: string;\n verifyRoute: string;\n thresholdPlan: ThresholdSessionPlan | null;\n warmPolicy: WarmSigningSessionPolicy;\n rollbackVrfOnFailure: () => Promise<void>;\n onEvent?: (event: LoginSSEvent) => void;\n onError?: (error: Error) => void;\n afterCall?: AfterCall<LoginAndCreateSessionResult>;\n}): Promise<LoginAndCreateSessionResult> {\n const {\n context,\n nearAccountId,\n baseLoginResult,\n baseUnlockCredential,\n preferredDeviceNumber,\n session,\n relayUrl,\n verifyRoute,\n thresholdPlan,\n warmPolicy,\n rollbackVrfOnFailure,\n onEvent,\n onError,\n afterCall,\n } = args;\n\n const { webAuthnManager } = context;\n\n try {\n const rpId = webAuthnManager.getRpId();\n if (!rpId) {\n throw new Error('Missing rpId for VRF challenge generation during login');\n }\n\n const blockInfo = await context.nearClient.viewBlock({ finality: 'final' });\n const txBlockHash = blockInfo?.header?.hash;\n const txBlockHeight = String(blockInfo.header?.height ?? '');\n const intentDigest = await computeLoginIntentDigest({ nearAccountId, rpId });\n const vrfChallenge = await webAuthnManager.generateVrfChallengeOnce({\n userId: nearAccountId,\n rpId,\n blockHash: txBlockHash,\n blockHeight: txBlockHeight,\n intentDigest,\n ...(thresholdPlan ? { sessionPolicyDigest32: thresholdPlan.policy.sessionPolicyDigest32 } : {}),\n });\n\n const authenticators = await webAuthnManager.getAuthenticatorsByUser(nearAccountId);\n const authenticatorsForPrompt = prioritizeAuthenticatorsByDeviceNumber(\n authenticators,\n preferredDeviceNumber\n );\n const credential = await webAuthnManager.getAuthenticationCredentialsSerialized({\n nearAccountId,\n challenge: vrfChallenge,\n allowCredentials: authenticatorsToAllowCredentials(authenticatorsForPrompt),\n });\n\n const effectiveLoginResult = await bindVrfToSelectedLoginPasskeyDevice({\n webAuthnManager,\n nearAccountId,\n authenticators,\n credential,\n baseUnlockCredential,\n baseLoginResult: baseLoginResult,\n });\n\n await mintWarmSigningSession({\n context,\n nearAccountId,\n onEvent,\n credential,\n ttlMs: warmPolicy.ttlMs,\n remainingUses: warmPolicy.remainingUses,\n });\n\n let serverSessionJwt: string | undefined;\n if (session) {\n const v = await verifyAuthenticationResponse(\n relayUrl,\n verifyRoute,\n session.kind,\n vrfChallenge,\n credential\n );\n if (!v.success || !v.verified) {\n const errMsg = v.error || 'Session verification failed';\n return await finalizeLoginError({\n message: errMsg,\n rollbackVrfOnFailure,\n onEvent,\n afterCall,\n callOnError: false,\n });\n }\n serverSessionJwt = v.jwt;\n }\n\n if (thresholdPlan) {\n await mintThresholdSessionBestEffort({\n context,\n nearAccountId,\n plan: thresholdPlan,\n vrfChallenge,\n credential,\n });\n }\n\n const loginResult: LoginResult = serverSessionJwt\n ? { ...effectiveLoginResult, jwt: serverSessionJwt }\n : effectiveLoginResult;\n\n return await finalizeLoginSuccess({\n webAuthnManager,\n nearAccountId,\n loginResult,\n onEvent,\n afterCall,\n });\n } catch (e: any) {\n console.error('[login] Failed to start session:', e);\n const errMsg =\n getUserFriendlyErrorMessage(e, 'login') ||\n e?.message ||\n 'Session verification failed';\n return await finalizeLoginError({\n message: errMsg,\n error: e,\n rollbackVrfOnFailure,\n onEvent,\n onError,\n afterCall,\n });\n }\n}\n\n/**\n * When multiple passkeys exist for the same account, the user can select any of them in the WebAuthn prompt.\n * If the VRF worker was auto-unlocked using a different device (e.g. Shamir 3-pass based on the \"latest\" row),\n * we must rebind the VRF worker to the same deviceNumber as the selected credential. Otherwise, WrapKeySeed\n * derivation can mix PRF.first(from selected credential) with vrf_sk(from a different device), which later\n * causes vault decryption failures (e.g. `aead::Error` during private key export).\n */\nasync function bindVrfToSelectedLoginPasskeyDevice(args: {\n webAuthnManager: PasskeyManagerContext['webAuthnManager'];\n nearAccountId: AccountId;\n authenticators: ClientAuthenticatorData[];\n credential: WebAuthnAuthenticationCredential;\n baseUnlockCredential?: WebAuthnAuthenticationCredential;\n baseLoginResult: LoginResult;\n}): Promise<LoginResult> {\n const {\n webAuthnManager,\n nearAccountId,\n authenticators,\n credential,\n baseUnlockCredential,\n baseLoginResult,\n } = args;\n\n // Start with the base login result (may reflect whichever VRF keypair was auto-unlocked).\n // If the user selects a different passkey below, update it to match the chosen device.\n let effectiveLoginResult = baseLoginResult;\n\n if (authenticators.length <= 1) {\n return effectiveLoginResult;\n }\n\n const rawId = credential.rawId;\n const matched = authenticators.find((a) => a.credentialId === rawId);\n const selectedDeviceNumber = matched?.deviceNumber ?? null;\n\n if (selectedDeviceNumber === null) {\n return effectiveLoginResult;\n }\n\n const userForDevice = await IndexedDBManager.clientDB\n .getUserByDevice(nearAccountId, selectedDeviceNumber)\n .catch(() => null);\n\n if (userForDevice?.clientNearPublicKey) {\n effectiveLoginResult = {\n ...effectiveLoginResult,\n clientNearPublicKey: userForDevice.clientNearPublicKey,\n };\n }\n\n const shouldRebindVrf = !baseUnlockCredential || baseUnlockCredential.rawId !== rawId;\n if (shouldRebindVrf) {\n const shamir = userForDevice?.serverEncryptedVrfKeypair;\n if (!shamir?.ciphertextVrfB64u || !shamir?.kek_s_b64u || !shamir?.serverKeyId) {\n throw new Error(\n `Missing serverEncryptedVrfKeypair for account ${nearAccountId} device ${selectedDeviceNumber}. ` +\n 'Open the wallet once online to refresh local state, then try again.'\n );\n }\n\n const unlock = await webAuthnManager.shamir3PassDecryptVrfKeypair({\n nearAccountId,\n kek_s_b64u: shamir.kek_s_b64u,\n ciphertextVrfB64u: shamir.ciphertextVrfB64u,\n serverKeyId: shamir.serverKeyId,\n });\n if (!unlock.success) {\n throw new Error(\n unlock.error ||\n 'Failed to bind VRF keypair to the passkey you selected. Please try again.'\n );\n }\n }\n\n // Persist the deviceNumber that the user actually selected so subsequent flows (export/signing)\n // use the correct vault entry.\n await webAuthnManager.setLastUser(nearAccountId, selectedDeviceNumber);\n // Best-effort: stamp the selected device as the one last used for login.\n await webAuthnManager.updateLastLogin(nearAccountId).catch(() => undefined);\n\n return effectiveLoginResult;\n}\n\n/**\n * Mint or refresh a \"warm signing session\" in the VRF worker.\n *\n * Notes:\n * - Reuses a previously collected WebAuthn credential when provided (e.g. TouchID fallback),\n * to avoid prompting the user twice in a single login flow.\n * - Session TTL/remainingUses policy is enforced by the VRF worker.\n */\nasync function mintWarmSigningSession(args: {\n context: PasskeyManagerContext;\n nearAccountId: AccountId;\n onEvent?: (event: LoginSSEvent) => void;\n credential?: WebAuthnAuthenticationCredential;\n ttlMs: number;\n remainingUses: number;\n}): Promise<void> {\n const { context, nearAccountId, onEvent, credential, ttlMs, remainingUses } = args;\n const { webAuthnManager } = context;\n\n onEvent?.({\n step: 3,\n phase: LoginPhase.STEP_3_VRF_UNLOCK,\n status: LoginStatus.PROGRESS,\n message: 'Unlocking warm signing session...'\n });\n\n // If we already performed a TouchID ceremony (e.g., VRF unlock fallback),\n // reuse that credential to avoid a second prompt.\n let effectiveCredential = credential;\n if (!effectiveCredential) {\n const challenge = createRandomVRFChallenge();\n const authenticators = await webAuthnManager.getAuthenticatorsByUser(nearAccountId);\n const { authenticatorsForPrompt } = await IndexedDBManager.clientDB.ensureCurrentPasskey(\n nearAccountId,\n authenticators,\n );\n effectiveCredential = await webAuthnManager.getAuthenticationCredentialsSerialized({\n nearAccountId,\n challenge: challenge as VRFChallenge,\n allowCredentials: authenticatorsToAllowCredentials(authenticatorsForPrompt),\n });\n }\n\n await webAuthnManager.mintSigningSessionFromCredential({\n nearAccountId,\n credential: effectiveCredential,\n ttlMs,\n remainingUses,\n });\n\n onEvent?.({\n step: 3,\n phase: LoginPhase.STEP_3_VRF_UNLOCK,\n status: LoginStatus.SUCCESS,\n message: 'Warm signing session unlocked'\n });\n}\n\n/**\n * Handle onchain (serverless) login using VRF flow per docs/vrf_challenges.md\n *\n * VRF AUTHENTICATION FLOW:\n * 1. Unlock VRF keypair in VRF Worker memory, either\n * - Decrypt via Shamir 3-pass (when Relayer is present), or\n * - Re-derive the VRF via credentials inside VRF worker dynamically\n * 2. Generate VRF challenge using stored VRF keypair + NEAR block data (no TouchID needed)\n * 3. Use VRF output as WebAuthn challenge for authentication\n * 4. Verify VRF proof and WebAuthn response on contract simultaneously\n * - VRF proof assures WebAuthn challenge is fresh and valid (replay protection)\n * - WebAuthn verification for origin + biometric credentials + device authenticity\n */\nasync function handleLoginUnlockVRF(\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n onEvent?: (event: LoginSSEvent) => void,\n onError?: (error: Error) => void,\n afterCall?: AfterCall<LoginAndCreateSessionResult>,\n deferCompletionHooks?: boolean,\n deviceNumberHint: number | null = null,\n): Promise<{\n result: LoginResult;\n usedFallbackTouchId: boolean;\n unlockCredential?: WebAuthnAuthenticationCredential;\n activeDeviceNumber: number | null;\n}> {\n const { webAuthnManager } = context;\n\n try {\n // Step 1: Get VRF credentials and authenticators, and validate them\n const hintUserPromise: Promise<ClientUserData | null> =\n deviceNumberHint !== null\n ? webAuthnManager.getUserByDevice(nearAccountId, deviceNumberHint).catch(() => null)\n : Promise.resolve(null);\n\n const [hintUser, lastUser, latestByAccount, authenticators] = await Promise.all([\n hintUserPromise,\n webAuthnManager.getLastUser(),\n IndexedDBManager.clientDB.getLastDBUpdatedUser(nearAccountId),\n webAuthnManager.getAuthenticatorsByUser(nearAccountId),\n ]);\n\n // Prefer the most recently updated record for this account; fall back to lastUser pointer.\n let userData: ClientUserData | null = null;\n if (hintUser && hintUser.nearAccountId === nearAccountId) {\n userData = hintUser;\n } else if (latestByAccount && latestByAccount.nearAccountId === nearAccountId) {\n userData = latestByAccount;\n } else if (lastUser && lastUser.nearAccountId === nearAccountId) {\n userData = lastUser;\n } else {\n userData = await webAuthnManager.getUserByDevice(nearAccountId, 1);\n }\n\n // Validate user data and authenticators\n if (!userData) {\n throw new Error(`User data not found for ${nearAccountId} in IndexedDB. Please register an account.`);\n }\n if (!userData.clientNearPublicKey) {\n throw new Error(`No NEAR public key found for ${nearAccountId}. Please register an account.`);\n }\n if (\n !userData.encryptedVrfKeypair?.encryptedVrfDataB64u ||\n !userData.encryptedVrfKeypair?.chacha20NonceB64u\n ) {\n throw new Error('No VRF credentials found. Please register an account.');\n }\n if (authenticators.length === 0) {\n throw new Error(`No authenticators found for account ${nearAccountId}. Please register.`);\n }\n\n // Step 2: Try Shamir 3-pass commutative unlock first (no TouchID required), fallback to TouchID\n onEvent?.({\n step: 2,\n phase: LoginPhase.STEP_2_WEBAUTHN_ASSERTION,\n status: LoginStatus.PROGRESS,\n message: 'Unlocking VRF keys...'\n });\n\n let unlockResult: { success: boolean; error?: string } = { success: false };\n let usedFallbackTouchId = false;\n let unlockCredential: WebAuthnAuthenticationCredential | undefined;\n let activeDeviceNumber = userData.deviceNumber;\n // Effective user row whose VRF/NEAR keys are actually used for this login.\n // May be switched when multiple devices exist and the user picks a different passkey.\n let effectiveUserData = userData;\n\n const shamir = userData.serverEncryptedVrfKeypair;\n const relayerUrl = context.configs.relayer?.url;\n const useShamir3PassVRFKeyUnlock = !!relayerUrl && !!shamir?.serverKeyId;\n\n if (useShamir3PassVRFKeyUnlock && shamir) {\n try {\n if (!shamir.ciphertextVrfB64u || !shamir.kek_s_b64u) {\n throw new Error('Missing Shamir3Pass fields (ciphertextVrfB64u/kek_s_b64u)');\n }\n\n unlockResult = await webAuthnManager.shamir3PassDecryptVrfKeypair({\n nearAccountId,\n kek_s_b64u: shamir.kek_s_b64u,\n ciphertextVrfB64u: shamir.ciphertextVrfB64u,\n serverKeyId: shamir.serverKeyId,\n });\n\n if (unlockResult.success) {\n const vrfStatus = await webAuthnManager.checkVrfStatus();\n const active = vrfStatus.active && vrfStatus.nearAccountId === nearAccountId;\n if (!active) {\n unlockResult = { success: false, error: 'VRF session inactive after Shamir3Pass' };\n }\n if (active) {\n // Proactive rotation if serverKeyId changed and we unlocked via Shamir\n await webAuthnManager.maybeProactiveShamirRefresh(nearAccountId);\n }\n } else {\n throw new Error(`Shamir3Pass auto-unlock failed: ${unlockResult.error}`);\n }\n } catch (error: any) {\n unlockResult = { success: false, error: error.message };\n }\n }\n\n // Fallback to TouchID if Shamir3Pass decryption failed\n if (!unlockResult.success) {\n const authenticatorsForPrompt = prioritizeAuthenticatorsByDeviceNumber(authenticators, deviceNumberHint);\n const fallback = await fallbackUnlockVrfKeypairWithTouchId({\n webAuthnManager,\n nearAccountId,\n authenticators: authenticatorsForPrompt,\n userData,\n onEvent,\n });\n unlockResult = fallback.unlockResult;\n usedFallbackTouchId = fallback.usedFallbackTouchId;\n unlockCredential = fallback.unlockCredential;\n effectiveUserData = fallback.effectiveUserData;\n activeDeviceNumber = fallback.activeDeviceNumber;\n }\n\n if (!unlockResult.success) {\n throw new Error(`Failed to unlock VRF keypair: ${unlockResult.error}`);\n }\n\n onEvent?.({\n step: 3,\n phase: LoginPhase.STEP_3_VRF_UNLOCK,\n status: LoginStatus.SUCCESS,\n message: 'VRF keypair unlocked...'\n });\n\n // Proactive refresh: if Shamir3Pass failed and we used TouchID, re-encrypt under current server key\n try {\n const relayerUrl = context.configs.relayer?.url;\n if (usedFallbackTouchId && relayerUrl) {\n const refreshed = await webAuthnManager.shamir3PassEncryptCurrentVrfKeypair();\n await webAuthnManager.updateServerEncryptedVrfKeypair(nearAccountId, refreshed, activeDeviceNumber);\n }\n } catch (refreshErr: any) {\n console.warn('Non-fatal: Failed to refresh serverEncryptedVrfKeypair:', refreshErr?.message || refreshErr);\n }\n\n // Step 3: Update local data and return success\n // Ensure last-user deviceNumber reflects the passkey actually used for login.\n try {\n await webAuthnManager.setLastUser(nearAccountId, activeDeviceNumber);\n } catch {\n // Non-fatal; continue even if last-user update fails.\n }\n await webAuthnManager.updateLastLogin(nearAccountId);\n\n const result: LoginResult = {\n success: true,\n loggedInNearAccountId: nearAccountId,\n // Ensure the clientNearPublicKey reflects the device whose VRF credentials\n // are actually active for this login.\n clientNearPublicKey: effectiveUserData.clientNearPublicKey,\n nearAccountId: nearAccountId\n };\n\n if (!deferCompletionHooks) {\n onEvent?.({\n step: 4,\n phase: LoginPhase.STEP_4_LOGIN_COMPLETE,\n status: LoginStatus.SUCCESS,\n message: 'Login completed successfully',\n nearAccountId: nearAccountId,\n clientNearPublicKey: effectiveUserData.clientNearPublicKey\n });\n afterCall?.(true, result);\n }\n return { result, usedFallbackTouchId, unlockCredential, activeDeviceNumber };\n\n } catch (error: any) {\n // Use centralized error handling\n const errorMessage = getUserFriendlyErrorMessage(error, 'login');\n\n onError?.(error);\n onEvent?.({\n step: 0,\n phase: LoginPhase.LOGIN_ERROR,\n status: LoginStatus.ERROR,\n message: errorMessage,\n error: errorMessage\n });\n\n const result: LoginResult = { success: false, error: errorMessage };\n afterCall?.(false);\n return { result, usedFallbackTouchId: false, activeDeviceNumber: null };\n }\n}\n\n/**\n * TouchID fallback path for VRF unlock.\n *\n * Used when Shamir 3-pass auto-unlock fails/unavailable:\n * - Prompts WebAuthn to obtain a serialized credential with PRF.first + PRF.second.\n * - Aligns the local encrypted VRF keypair blob with the passkey the user actually chose\n * (important when multiple devices/passkeys exist for the same account).\n * - Unlocks the VRF keypair inside the VRF worker and returns updated effective user context.\n */\nasync function fallbackUnlockVrfKeypairWithTouchId(args: {\n webAuthnManager: PasskeyManagerContext['webAuthnManager'];\n nearAccountId: AccountId;\n authenticators: ClientAuthenticatorData[];\n userData: ClientUserData;\n onEvent?: (event: LoginSSEvent) => void;\n}): Promise<{\n unlockResult: { success: boolean; error?: string };\n usedFallbackTouchId: boolean;\n unlockCredential: WebAuthnAuthenticationCredential;\n effectiveUserData: ClientUserData;\n activeDeviceNumber: number;\n}> {\n const { webAuthnManager, nearAccountId, authenticators, userData, onEvent } = args;\n\n onEvent?.({\n step: 2,\n phase: LoginPhase.STEP_2_WEBAUTHN_ASSERTION,\n status: LoginStatus.PROGRESS,\n message: 'Logging in, unlocking VRF credentials...'\n });\n\n const challenge = createRandomVRFChallenge();\n const credential = await webAuthnManager.getAuthenticationCredentialsSerializedDualPrf({\n nearAccountId,\n challenge: challenge as VRFChallenge,\n credentialIds: authenticators.map((a) => a.credentialId),\n });\n\n let effectiveUserData = userData;\n let activeDeviceNumber = userData.deviceNumber;\n\n // If multiple authenticators exist, align VRF credentials with the passkey\n // the user actually chose, based on credentialId → deviceNumber.\n if (authenticators.length > 1) {\n const rawId = credential.rawId;\n const matched = authenticators.find(a => a.credentialId === rawId);\n if (matched) {\n try {\n const byDevice = await IndexedDBManager.clientDB.getUserByDevice(nearAccountId, matched.deviceNumber);\n if (byDevice) {\n effectiveUserData = byDevice;\n activeDeviceNumber = matched.deviceNumber;\n }\n } catch {\n // If lookup by device fails, fall back to the base userData.\n }\n }\n }\n\n const unlockResult = await webAuthnManager.unlockVRFKeypair({\n nearAccountId: nearAccountId,\n encryptedVrfKeypair: {\n encryptedVrfDataB64u: effectiveUserData.encryptedVrfKeypair.encryptedVrfDataB64u,\n chacha20NonceB64u: effectiveUserData.encryptedVrfKeypair.chacha20NonceB64u,\n },\n credential: credential,\n });\n\n return {\n unlockResult,\n usedFallbackTouchId: unlockResult.success,\n unlockCredential: credential,\n effectiveUserData,\n activeDeviceNumber,\n };\n}\n\n/**\n * High-level login snapshot used by React contexts/UI.\n *\n * Returns:\n * - `login`: derived from IndexedDB last-user pointer + VRF worker status\n * - `signingSession`: warm signing session status when available\n *\n * The `nearAccountId` argument is treated as a \"query hint\" and must match the\n * last logged-in account to be considered logged in (prevents accidental cross-account reads).\n */\nexport async function getLoginSession(\n context: PasskeyManagerContext,\n nearAccountId?: AccountId\n): Promise<LoginSession> {\n const login = await getLoginStateInternal(context, nearAccountId);\n if (!login?.isLoggedIn || !login.nearAccountId) return { login, signingSession: null };\n try {\n const signingSession = await context.webAuthnManager.getWarmSigningSessionStatus(login.nearAccountId);\n return { login, signingSession };\n } catch {\n return { login, signingSession: null };\n }\n}\n\n/**\n * Internal helper for computing `LoginState`.\n *\n * Implementation detail:\n * - Trusts the IndexedDB last-user pointer for account selection, then confirms\n * that the VRF worker is actually active for that same account.\n */\nasync function getLoginStateInternal(\n context: PasskeyManagerContext,\n nearAccountId?: AccountId\n): Promise<LoginState> {\n const { webAuthnManager } = context;\n try {\n // Determine target account strictly from the last logged-in device.\n const lastUser = await webAuthnManager.getLastUser();\n const targetAccountId = nearAccountId ?? lastUser?.nearAccountId ?? null;\n\n // If caller requested a specific account, it must match the last logged-in account.\n if (!lastUser || (targetAccountId && lastUser.nearAccountId !== targetAccountId)) {\n return {\n isLoggedIn: false,\n nearAccountId: targetAccountId || null,\n publicKey: null,\n vrfActive: false,\n userData: null\n };\n }\n\n const userData = lastUser;\n const publicKey = userData?.clientNearPublicKey || null;\n\n // Check actual VRF worker status\n const vrfStatus = await webAuthnManager.checkVrfStatus();\n const vrfActive = vrfStatus.active && vrfStatus.nearAccountId === targetAccountId;\n\n // Determine if user is considered \"logged in\"\n // User is logged in if they have user data and VRF is active\n const isLoggedIn = !!(userData && userData.clientNearPublicKey && vrfActive);\n\n return {\n isLoggedIn,\n nearAccountId: targetAccountId,\n publicKey,\n vrfActive,\n userData,\n vrfSessionDuration: vrfStatus.sessionDuration || 0\n };\n\n } catch (error: any) {\n console.warn('Error getting login state:', error);\n return {\n isLoggedIn: false,\n nearAccountId: nearAccountId || null,\n publicKey: null,\n vrfActive: false,\n userData: null\n };\n }\n}\n\n/**\n * List recently used accounts from IndexedDB.\n *\n * Used for account picker UIs and initial app bootstrap state.\n */\nexport async function getRecentLogins(\n context: PasskeyManagerContext\n): Promise<GetRecentLoginsResult> {\n const { webAuthnManager } = context;\n // Get all user accounts from IndexDB\n const allUsersData = await webAuthnManager.getAllUsers();\n const accountIds = allUsersData.map(user => user.nearAccountId);\n // Get last used account for initial state\n const lastUsedAccount = await webAuthnManager.getLastUser();\n return {\n accountIds,\n lastUsedAccount,\n };\n}\n\n/**\n * Clear the active VRF session and any client-side nonce caches.\n *\n * This is the canonical \"logout\" operation for the SDK (does not delete accounts).\n */\nexport async function logoutAndClearSession(context: PasskeyManagerContext): Promise<void> {\n const { webAuthnManager } = context;\n await webAuthnManager.clearVrfSession();\n try { webAuthnManager.getNonceManager().clear(); } catch {}\n try { clearAllCachedThresholdEd25519AuthSessions(); } catch {}\n}\n\nfunction prioritizeAuthenticatorsByDeviceNumber(\n authenticators: ClientAuthenticatorData[],\n deviceNumber: number | null\n): ClientAuthenticatorData[] {\n if (authenticators.length <= 1) return authenticators;\n if (deviceNumber === null) return authenticators;\n const preferred = authenticators.filter((a) => a.deviceNumber === deviceNumber);\n if (preferred.length === 0) return authenticators;\n const rest = authenticators.filter((a) => a.deviceNumber !== deviceNumber);\n return [...preferred, ...rest];\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"login.js","names":["LoginPhase","LoginStatus","parseDeviceNumber","err: any","getUserFriendlyErrorMessage","signingSession: SigningSessionStatus","IndexedDBManager","normalizeThresholdEd25519ParticipantIds","buildThresholdSessionPolicy","makeThresholdEd25519AuthSessionCacheKey","e: any","clientVerifyingShareB64u: string | null","mintThresholdEd25519AuthSession","computeLoginIntentDigest","authenticatorsToAllowCredentials","serverSessionJwt: string | undefined","verifyAuthenticationResponse","loginResult: LoginResult","createRandomVRFChallenge","hintUserPromise: Promise<ClientUserData | null>","userData: ClientUserData | null","unlockResult: { success: boolean; error?: string }","unlockCredential: WebAuthnAuthenticationCredential | undefined","error: any","relayerUrl","refreshErr: any","result: LoginResult"],"sources":["../../../../src/core/TatchiPasskey/login.ts"],"sourcesContent":["import type {\n AfterCall,\n LoginHooksOptions,\n LoginSSEvent,\n} from '../types/sdkSentEvents';\nimport { LoginPhase, LoginStatus } from '../types/sdkSentEvents';\nimport type {\n GetRecentLoginsResult,\n LoginAndCreateSessionResult,\n LoginResult,\n LoginSession,\n LoginState,\n SigningSessionStatus,\n} from '../types/tatchi';\nimport type { PasskeyManagerContext } from './index';\nimport type { AccountId } from '../types/accountIds';\nimport type { WebAuthnAuthenticationCredential } from '../types/webauthn';\nimport { getUserFriendlyErrorMessage } from '../../utils/errors';\nimport { createRandomVRFChallenge, VRFChallenge } from '../types/vrf-worker';\nimport { authenticatorsToAllowCredentials } from '../WebAuthnManager/touchIdPrompt';\nimport { IndexedDBManager } from '../IndexedDBManager';\nimport type { ClientAuthenticatorData, ClientUserData } from '../IndexedDBManager';\nimport { verifyAuthenticationResponse } from '../rpcCalls';\nimport { computeLoginIntentDigest } from '../digests/intentDigest';\nimport { buildThresholdSessionPolicy } from '../threshold/thresholdSessionPolicy';\nimport { parseDeviceNumber } from '../WebAuthnManager/SignerWorkerManager/getDeviceNumber';\nimport {\n clearAllCachedThresholdEd25519AuthSessions,\n makeThresholdEd25519AuthSessionCacheKey,\n mintThresholdEd25519AuthSession,\n putCachedThresholdEd25519AuthSession,\n} from '../threshold/thresholdEd25519AuthSession';\nimport { normalizeThresholdEd25519ParticipantIds } from '../../threshold/participants';\n\ntype WarmSigningSessionPolicy = { ttlMs: number; remainingUses: number };\n\ntype ThresholdSessionPlan = {\n sessionKind: 'jwt';\n relayerUrl: string;\n relayerKeyId: string;\n cacheKey: string;\n policy: Awaited<ReturnType<typeof buildThresholdSessionPolicy>>;\n deviceNumber: number;\n};\n\n/**\n * Core login function that handles passkey authentication without React dependencies.\n *\n * - Unlocks the VRF keypair (Shamir 3‑pass auto‑unlock when possible; falls back to TouchID).\n * - Updates local login state and returns success with account/public key info.\n * - Optional: mints a server session when `options.session` is provided.\n * - Generates a fresh, chain‑anchored VRF challenge (using latest block).\n * - Collects a WebAuthn assertion over the VRF output and posts to the relay route\n * (defaults to `/verify-authentication-response`).\n * - When `kind: 'jwt'`, returns the token in `result.jwt`.\n * - When `kind: 'cookie'`, the server sets an HttpOnly cookie and no JWT is returned.\n */\nexport async function loginAndCreateSession(\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n options?: LoginHooksOptions\n): Promise<LoginAndCreateSessionResult> {\n const { onEvent, onError, afterCall } = options || {};\n const { webAuthnManager } = context;\n\n onEvent?.({\n step: 1,\n phase: LoginPhase.STEP_1_PREPARATION,\n status: LoginStatus.PROGRESS,\n message: `Starting login for ${nearAccountId}`\n });\n\n const prevStatus = await webAuthnManager.checkVrfStatus();\n const prevVrfAccountId = prevStatus?.active ? prevStatus.nearAccountId : null;\n\n // If this call activates VRF then fails, clear the partial session.\n const rollbackVrfOnFailure = async () => {\n const status = await webAuthnManager.checkVrfStatus();\n const current = status?.active ? status.nearAccountId : null;\n if (current && current !== prevVrfAccountId) {\n await logoutAndClearSession(context);\n }\n };\n\n try {\n // Validation\n if (!window.isSecureContext) {\n const errorMessage = 'Passkey operations require a secure context (HTTPS or localhost).';\n return await finalizeLoginError({\n message: errorMessage,\n error: new Error(errorMessage),\n rollbackVrfOnFailure,\n onEvent,\n onError,\n afterCall,\n callAfterCall: false,\n });\n }\n\n const session = options?.session;\n const wantsServerSession = session !== undefined;\n const deviceNumberHint = parseDeviceNumber(options?.deviceNumber, { min: 1 });\n const base = await handleLoginUnlockVRF(\n context,\n nearAccountId,\n onEvent,\n onError,\n afterCall,\n // Defer final 'login-complete' event & afterCall until warm signing session is minted\n true,\n deviceNumberHint\n );\n // If base login failed, just return\n if (!base.result.success) {\n return base.result;\n }\n\n const preferredDeviceNumber = deviceNumberHint ?? base.activeDeviceNumber;\n const warmPolicy = resolveWarmSigningSessionPolicy(context, options);\n\n const wantsThresholdSession =\n webAuthnManager.getUserPreferences().getSignerMode().mode === 'threshold-signer';\n const relayUrl = (session?.relayUrl || context.configs.relayer.url).trim();\n const verifyRoute = (session?.route || '/verify-authentication-response').trim();\n\n const thresholdPlan = await prepareThresholdSessionPlan({\n context,\n nearAccountId,\n preferredDeviceNumber,\n relayUrl,\n ttlMs: warmPolicy.ttlMs,\n remainingUses: warmPolicy.remainingUses,\n wantsThresholdSession,\n });\n\n const wantsRelayerSession = wantsServerSession || thresholdPlan !== null;\n if (wantsRelayerSession && !relayUrl) {\n console.warn('[login] No relayUrl provided for session-style signing');\n }\n\n if (wantsRelayerSession && relayUrl) {\n return await runRelayerSessionFlow({\n context,\n nearAccountId,\n baseLoginResult: base.result,\n baseUnlockCredential: base.unlockCredential,\n preferredDeviceNumber,\n session,\n relayUrl,\n verifyRoute,\n thresholdPlan,\n warmPolicy,\n rollbackVrfOnFailure,\n onEvent,\n onError,\n afterCall,\n });\n }\n\n // No relayer session requested (or relayUrl missing): mint/refresh warm signing session only.\n await mintWarmSigningSession({\n context,\n nearAccountId,\n onEvent,\n credential: base.unlockCredential,\n ttlMs: warmPolicy.ttlMs,\n remainingUses: warmPolicy.remainingUses,\n });\n return await finalizeLoginSuccess({\n webAuthnManager,\n nearAccountId,\n loginResult: base.result,\n onEvent,\n afterCall,\n });\n } catch (err: any) {\n const errorMessage =\n getUserFriendlyErrorMessage(err, 'login') || err?.message || 'Login failed';\n return await finalizeLoginError({\n message: errorMessage,\n error: err,\n rollbackVrfOnFailure,\n onEvent,\n onError,\n afterCall,\n });\n }\n}\n\nfunction resolveWarmSigningSessionPolicy(\n context: PasskeyManagerContext,\n options?: LoginHooksOptions\n): WarmSigningSessionPolicy {\n const defaults = context.configs.signingSessionDefaults;\n return {\n ttlMs: options?.signingSession?.ttlMs ?? defaults.ttlMs,\n remainingUses: options?.signingSession?.remainingUses ?? defaults.remainingUses,\n };\n}\n\nasync function attachSigningSessionStatus(args: {\n webAuthnManager: PasskeyManagerContext['webAuthnManager'];\n nearAccountId: AccountId;\n loginResult: LoginResult;\n}): Promise<LoginAndCreateSessionResult> {\n const { webAuthnManager, nearAccountId, loginResult } = args;\n if (!loginResult.success) return loginResult;\n try {\n const signingSession: SigningSessionStatus =\n await webAuthnManager.getWarmSigningSessionStatus(nearAccountId);\n return { ...loginResult, signingSession };\n } catch {\n return loginResult;\n }\n}\n\nasync function finalizeLoginSuccess(args: {\n webAuthnManager: PasskeyManagerContext['webAuthnManager'];\n nearAccountId: AccountId;\n loginResult: LoginResult;\n onEvent?: (event: LoginSSEvent) => void;\n afterCall?: AfterCall<LoginAndCreateSessionResult>;\n}): Promise<LoginAndCreateSessionResult> {\n const { webAuthnManager, nearAccountId, loginResult, onEvent, afterCall } = args;\n const finalResult = await attachSigningSessionStatus({\n webAuthnManager,\n nearAccountId,\n loginResult,\n });\n onEvent?.({\n step: 4,\n phase: LoginPhase.STEP_4_LOGIN_COMPLETE,\n status: LoginStatus.SUCCESS,\n message: 'Login completed successfully',\n nearAccountId,\n clientNearPublicKey: loginResult.clientNearPublicKey ?? '',\n });\n await afterCall?.(true, finalResult);\n return finalResult;\n}\n\nasync function finalizeLoginError(args: {\n message: string;\n error?: unknown;\n rollbackVrfOnFailure: () => Promise<void>;\n onEvent?: (event: LoginSSEvent) => void;\n onError?: (error: Error) => void;\n afterCall?: AfterCall<LoginAndCreateSessionResult>;\n callOnError?: boolean;\n callAfterCall?: boolean;\n}): Promise<LoginAndCreateSessionResult> {\n const {\n message,\n error,\n rollbackVrfOnFailure,\n onEvent,\n onError,\n afterCall,\n callOnError = true,\n callAfterCall = true,\n } = args;\n\n try { await rollbackVrfOnFailure(); } catch {}\n\n if (callOnError) {\n onError?.(error as any);\n }\n\n onEvent?.({\n step: 0,\n phase: LoginPhase.LOGIN_ERROR,\n status: LoginStatus.ERROR,\n message,\n error: message,\n });\n\n if (callAfterCall) {\n await afterCall?.(false);\n }\n return { success: false, error: message };\n}\n\nasync function prepareThresholdSessionPlan(args: {\n context: PasskeyManagerContext;\n nearAccountId: AccountId;\n preferredDeviceNumber: number | null;\n relayUrl: string;\n ttlMs: number;\n remainingUses: number;\n wantsThresholdSession: boolean;\n}): Promise<ThresholdSessionPlan | null> {\n const {\n context,\n nearAccountId,\n preferredDeviceNumber,\n relayUrl,\n ttlMs,\n remainingUses,\n wantsThresholdSession,\n } = args;\n if (!wantsThresholdSession || !relayUrl) return null;\n if (!Number.isFinite(ttlMs) || ttlMs <= 0 || !Number.isFinite(remainingUses) || remainingUses <= 0) {\n console.debug(\n '[login] threshold session mint disabled (ttlMs/remainingUses must be > 0); skipping threshold session'\n );\n return null;\n }\n\n const { webAuthnManager } = context;\n\n try {\n const rpId = webAuthnManager.getRpId();\n if (!rpId) throw new Error('Missing rpId for threshold session');\n\n const lastUser = await webAuthnManager.getLastUser();\n const deviceNumber = preferredDeviceNumber ??\n (lastUser?.nearAccountId === nearAccountId\n ? lastUser.deviceNumber\n : (await IndexedDBManager.clientDB.getLastDBUpdatedUser(nearAccountId))\n ?.deviceNumber ?? null);\n\n if (deviceNumber === null) {\n console.warn('[login] threshold-signer configured but no threshold key material found; skipping threshold session');\n return null;\n }\n\n const thresholdKeyMaterial = await IndexedDBManager.nearKeysDB.getThresholdKeyMaterial(\n nearAccountId,\n deviceNumber\n );\n const relayerKeyId = thresholdKeyMaterial?.relayerKeyId || null;\n const participantIds = thresholdKeyMaterial?.participants?.map((p) => p.id) || null;\n const normalizedParticipantIds = normalizeThresholdEd25519ParticipantIds(participantIds);\n\n if (!relayerKeyId) {\n console.warn('[login] threshold-signer configured but no threshold key material found; skipping threshold session');\n return null;\n }\n\n if (!normalizedParticipantIds || normalizedParticipantIds.length < 2) {\n console.warn('[login] threshold key material missing/invalid participantIds; skipping threshold session mint');\n return null;\n }\n\n const policy = await buildThresholdSessionPolicy({\n nearAccountId,\n rpId,\n relayerKeyId,\n ...(normalizedParticipantIds?.length ? { participantIds: normalizedParticipantIds } : {}),\n ttlMs,\n remainingUses,\n });\n\n const cacheKey = makeThresholdEd25519AuthSessionCacheKey({\n nearAccountId,\n rpId,\n relayerUrl: relayUrl,\n relayerKeyId,\n ...(normalizedParticipantIds?.length ? { participantIds: normalizedParticipantIds } : {}),\n });\n\n return {\n sessionKind: 'jwt',\n relayerUrl: relayUrl,\n relayerKeyId,\n cacheKey,\n policy,\n deviceNumber,\n };\n } catch (e: any) {\n console.warn(\n '[login] failed to prepare threshold session policy; skipping threshold session mint:',\n e?.message || e\n );\n return null;\n }\n}\n\nasync function mintThresholdSessionBestEffort(args: {\n context: PasskeyManagerContext;\n nearAccountId: AccountId;\n plan: ThresholdSessionPlan;\n vrfChallenge: VRFChallenge;\n credential: WebAuthnAuthenticationCredential;\n}): Promise<void> {\n const { context, nearAccountId, plan, vrfChallenge, credential } = args;\n const { webAuthnManager } = context;\n\n let clientVerifyingShareB64u: string | null = null;\n try {\n const localKeyMaterial = await IndexedDBManager.nearKeysDB.getLocalKeyMaterial(\n nearAccountId,\n plan.deviceNumber\n );\n const wrapKeySalt = String(localKeyMaterial?.wrapKeySalt || '').trim();\n if (wrapKeySalt) {\n const derived =\n await webAuthnManager.deriveThresholdEd25519ClientVerifyingShareFromCredential({\n credential,\n nearAccountId,\n wrapKeySalt,\n });\n if (derived.success && derived.clientVerifyingShareB64u) {\n clientVerifyingShareB64u = derived.clientVerifyingShareB64u;\n }\n }\n } catch (e: any) {\n console.warn(\n '[login] failed to derive clientVerifyingShareB64u for threshold session mint:',\n e?.message || e\n );\n }\n\n if (!clientVerifyingShareB64u) {\n console.warn(\n '[login] threshold session mint skipped: missing clientVerifyingShareB64u'\n );\n return;\n }\n\n const minted = await mintThresholdEd25519AuthSession({\n relayerUrl: plan.relayerUrl,\n sessionKind: plan.sessionKind,\n relayerKeyId: plan.relayerKeyId,\n clientVerifyingShareB64u,\n sessionPolicy: plan.policy.policy,\n vrfChallenge,\n webauthnAuthentication: credential,\n });\n\n if (minted.ok && minted.jwt) {\n putCachedThresholdEd25519AuthSession(plan.cacheKey, {\n sessionKind: plan.sessionKind,\n policy: plan.policy.policy,\n policyJson: plan.policy.policyJson,\n sessionPolicyDigest32: plan.policy.sessionPolicyDigest32,\n jwt: minted.jwt,\n ...(minted.expiresAtMs ? { expiresAtMs: minted.expiresAtMs } : {}),\n });\n return;\n }\n\n if (!minted.ok) {\n console.warn(\n '[login] threshold session mint failed:',\n minted.code || minted.message || 'unknown error'\n );\n }\n}\n\nasync function runRelayerSessionFlow(args: {\n context: PasskeyManagerContext;\n nearAccountId: AccountId;\n baseLoginResult: LoginResult;\n baseUnlockCredential?: WebAuthnAuthenticationCredential;\n preferredDeviceNumber: number | null;\n session: LoginHooksOptions['session'] | undefined;\n relayUrl: string;\n verifyRoute: string;\n thresholdPlan: ThresholdSessionPlan | null;\n warmPolicy: WarmSigningSessionPolicy;\n rollbackVrfOnFailure: () => Promise<void>;\n onEvent?: (event: LoginSSEvent) => void;\n onError?: (error: Error) => void;\n afterCall?: AfterCall<LoginAndCreateSessionResult>;\n}): Promise<LoginAndCreateSessionResult> {\n const {\n context,\n nearAccountId,\n baseLoginResult,\n baseUnlockCredential,\n preferredDeviceNumber,\n session,\n relayUrl,\n verifyRoute,\n thresholdPlan,\n warmPolicy,\n rollbackVrfOnFailure,\n onEvent,\n onError,\n afterCall,\n } = args;\n\n const { webAuthnManager } = context;\n\n try {\n const rpId = webAuthnManager.getRpId();\n if (!rpId) {\n throw new Error('Missing rpId for VRF challenge generation during login');\n }\n\n const blockInfo = await context.nearClient.viewBlock({ finality: 'final' });\n const txBlockHash = blockInfo?.header?.hash;\n const txBlockHeight = String(blockInfo.header?.height ?? '');\n const intentDigest = await computeLoginIntentDigest({ nearAccountId, rpId });\n const vrfChallenge = await webAuthnManager.generateVrfChallengeOnce({\n userId: nearAccountId,\n rpId,\n blockHash: txBlockHash,\n blockHeight: txBlockHeight,\n intentDigest,\n ...(thresholdPlan ? { sessionPolicyDigest32: thresholdPlan.policy.sessionPolicyDigest32 } : {}),\n });\n\n const authenticators = await webAuthnManager.getAuthenticatorsByUser(nearAccountId);\n const authenticatorsForPrompt = prioritizeAuthenticatorsByDeviceNumber(\n authenticators,\n preferredDeviceNumber\n );\n const credential = await webAuthnManager.getAuthenticationCredentialsSerialized({\n nearAccountId,\n challenge: vrfChallenge,\n allowCredentials: authenticatorsToAllowCredentials(authenticatorsForPrompt),\n });\n\n const effectiveLoginResult = await bindVrfToSelectedLoginPasskeyDevice({\n webAuthnManager,\n nearAccountId,\n authenticators,\n credential,\n baseUnlockCredential,\n baseLoginResult: baseLoginResult,\n });\n\n await mintWarmSigningSession({\n context,\n nearAccountId,\n onEvent,\n credential,\n ttlMs: warmPolicy.ttlMs,\n remainingUses: warmPolicy.remainingUses,\n });\n\n let serverSessionJwt: string | undefined;\n if (session) {\n const v = await verifyAuthenticationResponse(\n relayUrl,\n verifyRoute,\n session.kind,\n vrfChallenge,\n credential\n );\n if (!v.success || !v.verified) {\n const errMsg = v.error || 'Session verification failed';\n return await finalizeLoginError({\n message: errMsg,\n rollbackVrfOnFailure,\n onEvent,\n afterCall,\n callOnError: false,\n });\n }\n serverSessionJwt = v.jwt;\n }\n\n if (thresholdPlan) {\n await mintThresholdSessionBestEffort({\n context,\n nearAccountId,\n plan: thresholdPlan,\n vrfChallenge,\n credential,\n });\n }\n\n const loginResult: LoginResult = serverSessionJwt\n ? { ...effectiveLoginResult, jwt: serverSessionJwt }\n : effectiveLoginResult;\n\n return await finalizeLoginSuccess({\n webAuthnManager,\n nearAccountId,\n loginResult,\n onEvent,\n afterCall,\n });\n } catch (e: any) {\n console.error('[login] Failed to start session:', e);\n const errMsg =\n getUserFriendlyErrorMessage(e, 'login') ||\n e?.message ||\n 'Session verification failed';\n return await finalizeLoginError({\n message: errMsg,\n error: e,\n rollbackVrfOnFailure,\n onEvent,\n onError,\n afterCall,\n });\n }\n}\n\n/**\n * When multiple passkeys exist for the same account, the user can select any of them in the WebAuthn prompt.\n * If the VRF worker was auto-unlocked using a different device (e.g. Shamir 3-pass based on the \"latest\" row),\n * we must rebind the VRF worker to the same deviceNumber as the selected credential. Otherwise, WrapKeySeed\n * derivation can mix PRF.first(from selected credential) with vrf_sk(from a different device), which later\n * causes vault decryption failures (e.g. `aead::Error` during private key export).\n */\nasync function bindVrfToSelectedLoginPasskeyDevice(args: {\n webAuthnManager: PasskeyManagerContext['webAuthnManager'];\n nearAccountId: AccountId;\n authenticators: ClientAuthenticatorData[];\n credential: WebAuthnAuthenticationCredential;\n baseUnlockCredential?: WebAuthnAuthenticationCredential;\n baseLoginResult: LoginResult;\n}): Promise<LoginResult> {\n const {\n webAuthnManager,\n nearAccountId,\n authenticators,\n credential,\n baseUnlockCredential,\n baseLoginResult,\n } = args;\n\n // Start with the base login result (may reflect whichever VRF keypair was auto-unlocked).\n // If the user selects a different passkey below, update it to match the chosen device.\n let effectiveLoginResult = baseLoginResult;\n\n if (authenticators.length <= 1) {\n return effectiveLoginResult;\n }\n\n const rawId = credential.rawId;\n const matched = authenticators.find((a) => a.credentialId === rawId);\n const selectedDeviceNumber = matched?.deviceNumber ?? null;\n\n if (selectedDeviceNumber === null) {\n return effectiveLoginResult;\n }\n\n const userForDevice = await IndexedDBManager.clientDB\n .getUserByDevice(nearAccountId, selectedDeviceNumber)\n .catch(() => null);\n\n if (userForDevice?.clientNearPublicKey) {\n effectiveLoginResult = {\n ...effectiveLoginResult,\n clientNearPublicKey: userForDevice.clientNearPublicKey,\n };\n }\n\n const shouldRebindVrf = !baseUnlockCredential || baseUnlockCredential.rawId !== rawId;\n if (shouldRebindVrf) {\n const shamir = userForDevice?.serverEncryptedVrfKeypair;\n if (!shamir?.ciphertextVrfB64u || !shamir?.kek_s_b64u || !shamir?.serverKeyId) {\n throw new Error(\n `Missing serverEncryptedVrfKeypair for account ${nearAccountId} device ${selectedDeviceNumber}. ` +\n 'Open the wallet once online to refresh local state, then try again.'\n );\n }\n\n const unlock = await webAuthnManager.shamir3PassDecryptVrfKeypair({\n nearAccountId,\n kek_s_b64u: shamir.kek_s_b64u,\n ciphertextVrfB64u: shamir.ciphertextVrfB64u,\n serverKeyId: shamir.serverKeyId,\n });\n if (!unlock.success) {\n throw new Error(\n unlock.error ||\n 'Failed to bind VRF keypair to the passkey you selected. Please try again.'\n );\n }\n }\n\n // Persist the deviceNumber that the user actually selected so subsequent flows (export/signing)\n // use the correct vault entry.\n await webAuthnManager.setLastUser(nearAccountId, selectedDeviceNumber);\n // Best-effort: stamp the selected device as the one last used for login.\n await webAuthnManager.updateLastLogin(nearAccountId).catch(() => undefined);\n\n return effectiveLoginResult;\n}\n\n/**\n * Mint or refresh a \"warm signing session\" in the VRF worker.\n *\n * Notes:\n * - Reuses a previously collected WebAuthn credential when provided (e.g. TouchID fallback),\n * to avoid prompting the user twice in a single login flow.\n * - Session TTL/remainingUses policy is enforced by the VRF worker.\n */\nasync function mintWarmSigningSession(args: {\n context: PasskeyManagerContext;\n nearAccountId: AccountId;\n onEvent?: (event: LoginSSEvent) => void;\n credential?: WebAuthnAuthenticationCredential;\n ttlMs: number;\n remainingUses: number;\n}): Promise<void> {\n const { context, nearAccountId, onEvent, credential, ttlMs, remainingUses } = args;\n const { webAuthnManager } = context;\n\n onEvent?.({\n step: 3,\n phase: LoginPhase.STEP_3_VRF_UNLOCK,\n status: LoginStatus.PROGRESS,\n message: 'Unlocking warm signing session...'\n });\n\n // If we already performed a TouchID ceremony (e.g., VRF unlock fallback),\n // reuse that credential to avoid a second prompt.\n let effectiveCredential = credential;\n if (!effectiveCredential) {\n const challenge = createRandomVRFChallenge();\n const authenticators = await webAuthnManager.getAuthenticatorsByUser(nearAccountId);\n const { authenticatorsForPrompt } = await IndexedDBManager.clientDB.ensureCurrentPasskey(\n nearAccountId,\n authenticators,\n );\n effectiveCredential = await webAuthnManager.getAuthenticationCredentialsSerialized({\n nearAccountId,\n challenge: challenge as VRFChallenge,\n allowCredentials: authenticatorsToAllowCredentials(authenticatorsForPrompt),\n });\n }\n\n await webAuthnManager.mintSigningSessionFromCredential({\n nearAccountId,\n credential: effectiveCredential,\n ttlMs,\n remainingUses,\n });\n\n onEvent?.({\n step: 3,\n phase: LoginPhase.STEP_3_VRF_UNLOCK,\n status: LoginStatus.SUCCESS,\n message: 'Warm signing session unlocked'\n });\n}\n\n/**\n * Handle onchain (serverless) login using VRF flow per docs/vrf_challenges.md\n *\n * VRF AUTHENTICATION FLOW:\n * 1. Unlock VRF keypair in VRF Worker memory, either\n * - Decrypt via Shamir 3-pass (when Relayer is present), or\n * - Re-derive the VRF via credentials inside VRF worker dynamically\n * 2. Generate VRF challenge using stored VRF keypair + NEAR block data (no TouchID needed)\n * 3. Use VRF output as WebAuthn challenge for authentication\n * 4. Verify VRF proof and WebAuthn response on contract simultaneously\n * - VRF proof assures WebAuthn challenge is fresh and valid (replay protection)\n * - WebAuthn verification for origin + biometric credentials + device authenticity\n */\nasync function handleLoginUnlockVRF(\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n onEvent?: (event: LoginSSEvent) => void,\n onError?: (error: Error) => void,\n afterCall?: AfterCall<LoginAndCreateSessionResult>,\n deferCompletionHooks?: boolean,\n deviceNumberHint: number | null = null,\n): Promise<{\n result: LoginResult;\n usedFallbackTouchId: boolean;\n unlockCredential?: WebAuthnAuthenticationCredential;\n activeDeviceNumber: number | null;\n}> {\n const { webAuthnManager } = context;\n\n try {\n // Step 1: Get VRF credentials and authenticators, and validate them\n const hintUserPromise: Promise<ClientUserData | null> =\n deviceNumberHint !== null\n ? webAuthnManager.getUserByDevice(nearAccountId, deviceNumberHint).catch(() => null)\n : Promise.resolve(null);\n\n const [hintUser, lastUser, latestByAccount, authenticators] = await Promise.all([\n hintUserPromise,\n webAuthnManager.getLastUser(),\n IndexedDBManager.clientDB.getLastDBUpdatedUser(nearAccountId),\n webAuthnManager.getAuthenticatorsByUser(nearAccountId),\n ]);\n\n // Prefer the most recently updated record for this account; fall back to lastUser pointer.\n let userData: ClientUserData | null = null;\n if (hintUser && hintUser.nearAccountId === nearAccountId) {\n userData = hintUser;\n } else if (latestByAccount && latestByAccount.nearAccountId === nearAccountId) {\n userData = latestByAccount;\n } else if (lastUser && lastUser.nearAccountId === nearAccountId) {\n userData = lastUser;\n } else {\n userData = await webAuthnManager.getUserByDevice(nearAccountId, 1);\n }\n\n // Validate user data and authenticators\n if (!userData) {\n throw new Error(`User data not found for ${nearAccountId} in IndexedDB. Please register an account.`);\n }\n if (!userData.clientNearPublicKey) {\n throw new Error(`No NEAR public key found for ${nearAccountId}. Please register an account.`);\n }\n if (\n !userData.encryptedVrfKeypair?.encryptedVrfDataB64u ||\n !userData.encryptedVrfKeypair?.chacha20NonceB64u\n ) {\n throw new Error('No VRF credentials found. Please register an account.');\n }\n if (authenticators.length === 0) {\n throw new Error(`No authenticators found for account ${nearAccountId}. Please register.`);\n }\n\n // Step 2: Try Shamir 3-pass commutative unlock first (no TouchID required), fallback to TouchID\n onEvent?.({\n step: 2,\n phase: LoginPhase.STEP_2_WEBAUTHN_ASSERTION,\n status: LoginStatus.PROGRESS,\n message: 'Unlocking VRF keys...'\n });\n\n let unlockResult: { success: boolean; error?: string } = { success: false };\n let usedFallbackTouchId = false;\n let unlockCredential: WebAuthnAuthenticationCredential | undefined;\n let activeDeviceNumber = userData.deviceNumber;\n // Effective user row whose VRF/NEAR keys are actually used for this login.\n // May be switched when multiple devices exist and the user picks a different passkey.\n let effectiveUserData = userData;\n\n const shamir = userData.serverEncryptedVrfKeypair;\n const relayerUrl = context.configs.relayer?.url;\n const useShamir3PassVRFKeyUnlock = !!relayerUrl && !!shamir?.serverKeyId;\n\n if (useShamir3PassVRFKeyUnlock && shamir) {\n try {\n if (!shamir.ciphertextVrfB64u || !shamir.kek_s_b64u) {\n throw new Error('Missing Shamir3Pass fields (ciphertextVrfB64u/kek_s_b64u)');\n }\n\n unlockResult = await webAuthnManager.shamir3PassDecryptVrfKeypair({\n nearAccountId,\n kek_s_b64u: shamir.kek_s_b64u,\n ciphertextVrfB64u: shamir.ciphertextVrfB64u,\n serverKeyId: shamir.serverKeyId,\n });\n\n if (unlockResult.success) {\n const vrfStatus = await webAuthnManager.checkVrfStatus();\n const active = vrfStatus.active && vrfStatus.nearAccountId === nearAccountId;\n if (!active) {\n unlockResult = { success: false, error: 'VRF session inactive after Shamir3Pass' };\n }\n if (active) {\n // Proactive rotation if serverKeyId changed and we unlocked via Shamir\n await webAuthnManager.maybeProactiveShamirRefresh(nearAccountId);\n }\n } else {\n throw new Error(`Shamir3Pass auto-unlock failed: ${unlockResult.error}`);\n }\n } catch (error: any) {\n unlockResult = { success: false, error: error.message };\n }\n }\n\n // Fallback to TouchID if Shamir3Pass decryption failed\n if (!unlockResult.success) {\n const authenticatorsForPrompt = prioritizeAuthenticatorsByDeviceNumber(authenticators, deviceNumberHint);\n const fallback = await fallbackUnlockVrfKeypairWithTouchId({\n webAuthnManager,\n nearAccountId,\n authenticators: authenticatorsForPrompt,\n userData,\n onEvent,\n });\n unlockResult = fallback.unlockResult;\n usedFallbackTouchId = fallback.usedFallbackTouchId;\n unlockCredential = fallback.unlockCredential;\n effectiveUserData = fallback.effectiveUserData;\n activeDeviceNumber = fallback.activeDeviceNumber;\n }\n\n if (!unlockResult.success) {\n throw new Error(`Failed to unlock VRF keypair: ${unlockResult.error}`);\n }\n\n onEvent?.({\n step: 3,\n phase: LoginPhase.STEP_3_VRF_UNLOCK,\n status: LoginStatus.SUCCESS,\n message: 'VRF keypair unlocked...'\n });\n\n // Proactive refresh: if Shamir3Pass failed and we used TouchID, re-encrypt under current server key\n try {\n const relayerUrl = context.configs.relayer?.url;\n if (usedFallbackTouchId && relayerUrl) {\n const refreshed = await webAuthnManager.shamir3PassEncryptCurrentVrfKeypair();\n await webAuthnManager.updateServerEncryptedVrfKeypair(nearAccountId, refreshed, activeDeviceNumber);\n }\n } catch (refreshErr: any) {\n console.warn('Non-fatal: Failed to refresh serverEncryptedVrfKeypair:', refreshErr?.message || refreshErr);\n }\n\n // Step 3: Update local data and return success\n // Ensure last-user deviceNumber reflects the passkey actually used for login.\n try {\n await webAuthnManager.setLastUser(nearAccountId, activeDeviceNumber);\n } catch {\n // Non-fatal; continue even if last-user update fails.\n }\n await webAuthnManager.updateLastLogin(nearAccountId);\n\n const result: LoginResult = {\n success: true,\n loggedInNearAccountId: nearAccountId,\n // Ensure the clientNearPublicKey reflects the device whose VRF credentials\n // are actually active for this login.\n clientNearPublicKey: effectiveUserData.clientNearPublicKey,\n nearAccountId: nearAccountId\n };\n\n if (!deferCompletionHooks) {\n onEvent?.({\n step: 4,\n phase: LoginPhase.STEP_4_LOGIN_COMPLETE,\n status: LoginStatus.SUCCESS,\n message: 'Login completed successfully',\n nearAccountId: nearAccountId,\n clientNearPublicKey: effectiveUserData.clientNearPublicKey\n });\n afterCall?.(true, result);\n }\n return { result, usedFallbackTouchId, unlockCredential, activeDeviceNumber };\n\n } catch (error: any) {\n // Use centralized error handling\n const errorMessage = getUserFriendlyErrorMessage(error, 'login');\n\n onError?.(error);\n onEvent?.({\n step: 0,\n phase: LoginPhase.LOGIN_ERROR,\n status: LoginStatus.ERROR,\n message: errorMessage,\n error: errorMessage\n });\n\n const result: LoginResult = { success: false, error: errorMessage };\n afterCall?.(false);\n return { result, usedFallbackTouchId: false, activeDeviceNumber: null };\n }\n}\n\n/**\n * TouchID fallback path for VRF unlock.\n *\n * Used when Shamir 3-pass auto-unlock fails/unavailable:\n * - Prompts WebAuthn to obtain a serialized credential with PRF.first + PRF.second.\n * - Aligns the local encrypted VRF keypair blob with the passkey the user actually chose\n * (important when multiple devices/passkeys exist for the same account).\n * - Unlocks the VRF keypair inside the VRF worker and returns updated effective user context.\n */\nasync function fallbackUnlockVrfKeypairWithTouchId(args: {\n webAuthnManager: PasskeyManagerContext['webAuthnManager'];\n nearAccountId: AccountId;\n authenticators: ClientAuthenticatorData[];\n userData: ClientUserData;\n onEvent?: (event: LoginSSEvent) => void;\n}): Promise<{\n unlockResult: { success: boolean; error?: string };\n usedFallbackTouchId: boolean;\n unlockCredential: WebAuthnAuthenticationCredential;\n effectiveUserData: ClientUserData;\n activeDeviceNumber: number;\n}> {\n const { webAuthnManager, nearAccountId, authenticators, userData, onEvent } = args;\n\n onEvent?.({\n step: 2,\n phase: LoginPhase.STEP_2_WEBAUTHN_ASSERTION,\n status: LoginStatus.PROGRESS,\n message: 'Logging in, unlocking VRF credentials...'\n });\n\n const challenge = createRandomVRFChallenge();\n const credential = await webAuthnManager.getAuthenticationCredentialsSerializedDualPrf({\n nearAccountId,\n challenge: challenge as VRFChallenge,\n credentialIds: authenticators.map((a) => a.credentialId),\n });\n\n let effectiveUserData = userData;\n let activeDeviceNumber = userData.deviceNumber;\n\n // If multiple authenticators exist, align VRF credentials with the passkey\n // the user actually chose, based on credentialId → deviceNumber.\n if (authenticators.length > 1) {\n const rawId = credential.rawId;\n const matched = authenticators.find(a => a.credentialId === rawId);\n if (matched) {\n try {\n const byDevice = await IndexedDBManager.clientDB.getUserByDevice(nearAccountId, matched.deviceNumber);\n if (byDevice) {\n effectiveUserData = byDevice;\n activeDeviceNumber = matched.deviceNumber;\n }\n } catch {\n // If lookup by device fails, fall back to the base userData.\n }\n }\n }\n\n const unlockResult = await webAuthnManager.unlockVRFKeypair({\n nearAccountId: nearAccountId,\n encryptedVrfKeypair: {\n encryptedVrfDataB64u: effectiveUserData.encryptedVrfKeypair.encryptedVrfDataB64u,\n chacha20NonceB64u: effectiveUserData.encryptedVrfKeypair.chacha20NonceB64u,\n },\n credential: credential,\n });\n\n return {\n unlockResult,\n usedFallbackTouchId: unlockResult.success,\n unlockCredential: credential,\n effectiveUserData,\n activeDeviceNumber,\n };\n}\n\n/**\n * High-level login snapshot used by React contexts/UI.\n *\n * Returns:\n * - `login`: derived from IndexedDB last-user pointer + VRF worker status\n * - `signingSession`: warm signing session status when available\n *\n * The `nearAccountId` argument is treated as a \"query hint\" and must match the\n * last logged-in account to be considered logged in (prevents accidental cross-account reads).\n */\nexport async function getLoginSession(\n context: PasskeyManagerContext,\n nearAccountId?: AccountId\n): Promise<LoginSession> {\n const login = await getLoginStateInternal(context, nearAccountId);\n if (!login?.isLoggedIn || !login.nearAccountId) return { login, signingSession: null };\n try {\n const signingSession = await context.webAuthnManager.getWarmSigningSessionStatus(login.nearAccountId);\n return { login, signingSession };\n } catch {\n return { login, signingSession: null };\n }\n}\n\n/**\n * Internal helper for computing `LoginState`.\n *\n * Implementation detail:\n * - Trusts the IndexedDB last-user pointer for account selection, then confirms\n * that the VRF worker is actually active for that same account.\n */\nasync function getLoginStateInternal(\n context: PasskeyManagerContext,\n nearAccountId?: AccountId\n): Promise<LoginState> {\n const { webAuthnManager } = context;\n try {\n // Determine target account strictly from the last logged-in device.\n const lastUser = await webAuthnManager.getLastUser();\n const targetAccountId = nearAccountId ?? lastUser?.nearAccountId ?? null;\n\n // If caller requested a specific account, it must match the last logged-in account.\n if (!lastUser || (targetAccountId && lastUser.nearAccountId !== targetAccountId)) {\n return {\n isLoggedIn: false,\n nearAccountId: targetAccountId || null,\n publicKey: null,\n vrfActive: false,\n userData: null\n };\n }\n\n const userData = lastUser;\n const publicKey = userData?.clientNearPublicKey || null;\n\n // Check actual VRF worker status\n const vrfStatus = await webAuthnManager.checkVrfStatus();\n const vrfActive = vrfStatus.active && vrfStatus.nearAccountId === targetAccountId;\n\n // Determine if user is considered \"logged in\"\n // User is logged in if they have user data and VRF is active\n const isLoggedIn = !!(userData && userData.clientNearPublicKey && vrfActive);\n\n return {\n isLoggedIn,\n nearAccountId: targetAccountId,\n publicKey,\n vrfActive,\n userData,\n vrfSessionDuration: vrfStatus.sessionDuration || 0\n };\n\n } catch (error: any) {\n console.warn('Error getting login state:', error);\n return {\n isLoggedIn: false,\n nearAccountId: nearAccountId || null,\n publicKey: null,\n vrfActive: false,\n userData: null\n };\n }\n}\n\n/**\n * List recently used accounts from IndexedDB.\n *\n * Used for account picker UIs and initial app bootstrap state.\n */\nexport async function getRecentLogins(\n context: PasskeyManagerContext\n): Promise<GetRecentLoginsResult> {\n const { webAuthnManager } = context;\n // Get all user accounts from IndexDB\n const allUsersData = await webAuthnManager.getAllUsers();\n const accountIds = allUsersData.map(user => user.nearAccountId);\n // Get last used account for initial state\n const lastUsedAccount = await webAuthnManager.getLastUser();\n return {\n accountIds,\n lastUsedAccount,\n };\n}\n\n/**\n * Clear the active VRF session and any client-side nonce caches.\n *\n * This is the canonical \"logout\" operation for the SDK (does not delete accounts).\n */\nexport async function logoutAndClearSession(context: PasskeyManagerContext): Promise<void> {\n const { webAuthnManager } = context;\n // Best-effort VRF logout. Do not block logout indefinitely if the VRF worker is\n // stuck (e.g., an in-flight confirmation/signing flow).\n try {\n const clear = webAuthnManager.clearVrfSession().catch(() => undefined);\n await Promise.race([clear, new Promise<void>((resolve) => setTimeout(resolve, 2000))]);\n } catch {}\n // Ensure all ephemeral signing state is torn down so the next login starts cleanly.\n try { webAuthnManager.resetSigningState(); } catch {}\n try { webAuthnManager.getNonceManager().clear(); } catch {}\n try { clearAllCachedThresholdEd25519AuthSessions(); } catch {}\n}\n\nfunction prioritizeAuthenticatorsByDeviceNumber(\n authenticators: ClientAuthenticatorData[],\n deviceNumber: number | null\n): ClientAuthenticatorData[] {\n if (authenticators.length <= 1) return authenticators;\n if (deviceNumber === null) return authenticators;\n const preferred = authenticators.filter((a) => a.deviceNumber === deviceNumber);\n if (preferred.length === 0) return authenticators;\n const rest = authenticators.filter((a) => a.deviceNumber !== deviceNumber);\n return [...preferred, ...rest];\n}\n"],"mappings":"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"}
@@ -102,6 +102,40 @@ var SignerWorkerManager = class {
102
102
  MAX_WORKER_POOL_SIZE = 3;
103
103
  signingSessions = /* @__PURE__ */ new Map();
104
104
  SIGNING_SESSION_TIMEOUT_MS = 300 * 1e3;
105
+ sendQueueByWorker = /* @__PURE__ */ new WeakMap();
106
+ enqueueOnWorker(worker, task) {
107
+ const prev = this.sendQueueByWorker.get(worker) ?? Promise.resolve();
108
+ const next = prev.catch(() => void 0).then(task);
109
+ this.sendQueueByWorker.set(worker, next);
110
+ next.finally(() => {
111
+ if (this.sendQueueByWorker.get(worker) === next) this.sendQueueByWorker.delete(worker);
112
+ });
113
+ return next;
114
+ }
115
+ /**
116
+ * Force-terminate all worker instances and session state.
117
+ *
118
+ * Intended for explicit logout flows where we want to:
119
+ * - cancel any in-flight signing operations
120
+ * - close any session MessagePorts
121
+ * - zeroize worker memory by terminating workers
122
+ */
123
+ reset() {
124
+ for (const [sessionId, entry] of Array.from(this.signingSessions.entries())) {
125
+ try {
126
+ entry.wrapKeySeedPort?.close();
127
+ } catch {}
128
+ try {
129
+ entry.worker.terminate();
130
+ } catch {}
131
+ this.signingSessions.delete(sessionId);
132
+ }
133
+ for (const worker of this.workerPool) try {
134
+ worker.terminate();
135
+ } catch {}
136
+ this.workerPool = [];
137
+ this.sendQueueByWorker = /* @__PURE__ */ new WeakMap();
138
+ }
105
139
  getWorkerFromPool() {
106
140
  if (this.workerPool.length > 0) return this.workerPool.pop();
107
141
  return this.createSecureWorker();
@@ -259,7 +293,7 @@ var SignerWorkerManager = class {
259
293
  const finalPayload = effectiveSessionId ? require_session.withSessionId(effectiveSessionId, message.payload) : message.payload;
260
294
  const worker = sessionEntry ? sessionEntry.worker : this.getWorkerFromPool();
261
295
  const isSessionWorker = !!sessionEntry;
262
- return new Promise((resolve, reject) => {
296
+ return this.enqueueOnWorker(worker, () => new Promise((resolve, reject) => {
263
297
  const timeoutId = setTimeout(() => {
264
298
  try {
265
299
  if (isSessionWorker && effectiveSessionId) this.releaseSigningSession(effectiveSessionId);
@@ -337,7 +371,7 @@ var SignerWorkerManager = class {
337
371
  payload: finalPayload
338
372
  };
339
373
  worker.postMessage(formattedMessage);
340
- });
374
+ }));
341
375
  }
342
376
  /**
343
377
  * Derive NEAR keypair from a serialized WebAuthn registration credential