@tatchi-xyz/sdk 0.19.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/core/EmailRecovery/index.js +25 -0
- package/dist/cjs/core/EmailRecovery/index.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/emailRecovery.js +135 -77
- package/dist/cjs/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/index.js +2 -1
- package/dist/cjs/core/TatchiPasskey/index.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/linkDevice.js +2 -1
- package/dist/cjs/core/TatchiPasskey/linkDevice.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/scanDevice.js +5 -3
- package/dist/cjs/core/TatchiPasskey/scanDevice.js.map +1 -1
- package/dist/cjs/core/WalletIframe/client/router.js +1 -1
- package/dist/cjs/core/WalletIframe/client/router.js.map +1 -1
- package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js +3 -4
- package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js.map +1 -1
- package/dist/cjs/core/defaultConfigs.js +3 -7
- package/dist/cjs/core/defaultConfigs.js.map +1 -1
- package/dist/cjs/core/nearCrypto.js +29 -5
- package/dist/cjs/core/nearCrypto.js.map +1 -1
- package/dist/cjs/core/rpcCalls.js +56 -26
- package/dist/cjs/core/rpcCalls.js.map +1 -1
- package/dist/cjs/core/types/emailRecovery.js +33 -0
- package/dist/cjs/core/types/emailRecovery.js.map +1 -0
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{LinkedDevicesModal-CSSowiHP.css → LinkedDevicesModal-BRtht0XI.css} +1 -1
- package/dist/{esm/react/components/AccountMenuButton/LinkedDevicesModal-CSSowiHP.css.map → cjs/react/components/AccountMenuButton/LinkedDevicesModal-BRtht0XI.css.map} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{ProfileDropdown-CEPMZ1gY.css → ProfileDropdown-BG_6hcim.css} +1 -1
- package/dist/{esm/react/components/AccountMenuButton/ProfileDropdown-CEPMZ1gY.css.map → cjs/react/components/AccountMenuButton/ProfileDropdown-BG_6hcim.css.map} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{Web3AuthProfileButton-DopOg7Xc.css → Web3AuthProfileButton-k8_FAYFq.css} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{Web3AuthProfileButton-DopOg7Xc.css.map → Web3AuthProfileButton-k8_FAYFq.css.map} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/icons/{TouchIcon-BQWentvJ.css → TouchIcon-C-RcGfr5.css} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/icons/{TouchIcon-BQWentvJ.css.map → TouchIcon-C-RcGfr5.css.map} +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-DwrzWMYx.css → PasskeyAuthMenu-DKMiLeT9.css} +59 -4
- package/dist/cjs/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-DwrzWMYx.css.map → PasskeyAuthMenu-DKMiLeT9.css.map} +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/adapters/tatchi.js +1 -0
- package/dist/cjs/react/components/PasskeyAuthMenu/adapters/tatchi.js.map +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/client.js +30 -8
- package/dist/cjs/react/components/PasskeyAuthMenu/client.js.map +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/controller/useSDKEvents.js +22 -0
- package/dist/cjs/react/components/PasskeyAuthMenu/controller/useSDKEvents.js.map +1 -0
- package/dist/cjs/react/components/PasskeyAuthMenu/ui/ContentSwitcher.js +17 -4
- package/dist/cjs/react/components/PasskeyAuthMenu/ui/ContentSwitcher.js.map +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js +354 -154
- package/dist/cjs/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js.map +1 -1
- package/dist/cjs/react/components/{ShowQRCode-CCN4h6Uv.css → ShowQRCode-CB0UCQ_h.css} +1 -1
- package/dist/cjs/react/components/{ShowQRCode-CCN4h6Uv.css.map → ShowQRCode-CB0UCQ_h.css.map} +1 -1
- package/dist/cjs/react/context/useSDKFlowRuntime.js +183 -0
- package/dist/cjs/react/context/useSDKFlowRuntime.js.map +1 -0
- package/dist/cjs/react/context/useTatchiContextValue.js +24 -15
- package/dist/cjs/react/context/useTatchiContextValue.js.map +1 -1
- package/dist/cjs/react/context/useTatchiWithSdkFlow.js +96 -0
- package/dist/cjs/react/context/useTatchiWithSdkFlow.js.map +1 -0
- package/dist/cjs/react/sdk/src/core/EmailRecovery/index.js +26 -0
- package/dist/cjs/react/sdk/src/core/EmailRecovery/index.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/emailRecovery.js +135 -77
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/index.js +2 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/index.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/linkDevice.js +2 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/linkDevice.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/scanDevice.js +5 -3
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/scanDevice.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/WalletIframe/client/router.js +1 -1
- package/dist/cjs/react/sdk/src/core/WalletIframe/client/router.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js +3 -4
- package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/defaultConfigs.js +3 -7
- package/dist/cjs/react/sdk/src/core/defaultConfigs.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/nearCrypto.js +29 -5
- package/dist/cjs/react/sdk/src/core/nearCrypto.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/rpcCalls.js +56 -26
- package/dist/cjs/react/sdk/src/core/rpcCalls.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/types/emailRecovery.js +33 -0
- package/dist/cjs/react/sdk/src/core/types/emailRecovery.js.map +1 -0
- package/dist/cjs/server/email-recovery/emailParsers.js +2 -1
- package/dist/cjs/server/email-recovery/emailParsers.js.map +1 -1
- package/dist/cjs/server/email-recovery/index.js +6 -6
- package/dist/cjs/server/email-recovery/index.js.map +1 -1
- package/dist/cjs/server/email-recovery/rpcCalls.js +22 -3
- package/dist/cjs/server/email-recovery/rpcCalls.js.map +1 -1
- package/dist/cjs/server/router/cloudflare.js +8 -3
- package/dist/cjs/server/router/cloudflare.js.map +1 -1
- package/dist/cjs/server/router/express.js.map +1 -1
- package/dist/cjs/server/sdk/src/core/defaultConfigs.js +2 -4
- package/dist/cjs/server/sdk/src/core/defaultConfigs.js.map +1 -1
- package/dist/cjs/server/sdk/src/core/nearCrypto.js +26 -7
- package/dist/cjs/server/sdk/src/core/nearCrypto.js.map +1 -1
- package/dist/esm/core/EmailRecovery/index.js +25 -1
- package/dist/esm/core/EmailRecovery/index.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/emailRecovery.js +136 -78
- package/dist/esm/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/index.js +2 -1
- package/dist/esm/core/TatchiPasskey/index.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/linkDevice.js +2 -1
- package/dist/esm/core/TatchiPasskey/linkDevice.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/scanDevice.js +5 -3
- package/dist/esm/core/TatchiPasskey/scanDevice.js.map +1 -1
- package/dist/esm/core/WalletIframe/client/router.js +1 -1
- package/dist/esm/core/WalletIframe/client/router.js.map +1 -1
- package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js +2 -3
- package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js.map +1 -1
- package/dist/esm/core/defaultConfigs.js +3 -7
- package/dist/esm/core/defaultConfigs.js.map +1 -1
- package/dist/esm/core/nearCrypto.js +24 -6
- package/dist/esm/core/nearCrypto.js.map +1 -1
- package/dist/esm/core/rpcCalls.js +56 -26
- package/dist/esm/core/rpcCalls.js.map +1 -1
- package/dist/esm/core/types/emailRecovery.js +26 -0
- package/dist/esm/core/types/emailRecovery.js.map +1 -0
- package/dist/esm/index.js +3 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/react/components/AccountMenuButton/{LinkedDevicesModal-CSSowiHP.css → LinkedDevicesModal-BRtht0XI.css} +1 -1
- package/dist/{cjs/react/components/AccountMenuButton/LinkedDevicesModal-CSSowiHP.css.map → esm/react/components/AccountMenuButton/LinkedDevicesModal-BRtht0XI.css.map} +1 -1
- package/dist/esm/react/components/AccountMenuButton/{ProfileDropdown-CEPMZ1gY.css → ProfileDropdown-BG_6hcim.css} +1 -1
- package/dist/{cjs/react/components/AccountMenuButton/ProfileDropdown-CEPMZ1gY.css.map → esm/react/components/AccountMenuButton/ProfileDropdown-BG_6hcim.css.map} +1 -1
- package/dist/esm/react/components/AccountMenuButton/{Web3AuthProfileButton-DopOg7Xc.css → Web3AuthProfileButton-k8_FAYFq.css} +1 -1
- package/dist/esm/react/components/AccountMenuButton/{Web3AuthProfileButton-DopOg7Xc.css.map → Web3AuthProfileButton-k8_FAYFq.css.map} +1 -1
- package/dist/esm/react/components/AccountMenuButton/icons/{TouchIcon-BQWentvJ.css → TouchIcon-C-RcGfr5.css} +1 -1
- package/dist/esm/react/components/AccountMenuButton/icons/{TouchIcon-BQWentvJ.css.map → TouchIcon-C-RcGfr5.css.map} +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-DwrzWMYx.css → PasskeyAuthMenu-DKMiLeT9.css} +59 -4
- package/dist/esm/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-DwrzWMYx.css.map → PasskeyAuthMenu-DKMiLeT9.css.map} +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/adapters/tatchi.js +1 -0
- package/dist/esm/react/components/PasskeyAuthMenu/adapters/tatchi.js.map +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/client.js +30 -8
- package/dist/esm/react/components/PasskeyAuthMenu/client.js.map +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/controller/useSDKEvents.js +20 -0
- package/dist/esm/react/components/PasskeyAuthMenu/controller/useSDKEvents.js.map +1 -0
- package/dist/esm/react/components/PasskeyAuthMenu/ui/ContentSwitcher.js +17 -4
- package/dist/esm/react/components/PasskeyAuthMenu/ui/ContentSwitcher.js.map +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js +354 -154
- package/dist/esm/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js.map +1 -1
- package/dist/esm/react/components/{ShowQRCode-CCN4h6Uv.css → ShowQRCode-CB0UCQ_h.css} +1 -1
- package/dist/esm/react/components/{ShowQRCode-CCN4h6Uv.css.map → ShowQRCode-CB0UCQ_h.css.map} +1 -1
- package/dist/esm/react/context/useSDKFlowRuntime.js +181 -0
- package/dist/esm/react/context/useSDKFlowRuntime.js.map +1 -0
- package/dist/esm/react/context/useTatchiContextValue.js +25 -16
- package/dist/esm/react/context/useTatchiContextValue.js.map +1 -1
- package/dist/esm/react/context/useTatchiWithSdkFlow.js +94 -0
- package/dist/esm/react/context/useTatchiWithSdkFlow.js.map +1 -0
- package/dist/esm/react/sdk/src/core/EmailRecovery/index.js +25 -1
- package/dist/esm/react/sdk/src/core/EmailRecovery/index.js.map +1 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/emailRecovery.js +136 -78
- package/dist/esm/react/sdk/src/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/index.js +2 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/index.js.map +1 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/linkDevice.js +2 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/linkDevice.js.map +1 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/scanDevice.js +5 -3
- package/dist/esm/react/sdk/src/core/TatchiPasskey/scanDevice.js.map +1 -1
- package/dist/esm/react/sdk/src/core/WalletIframe/client/router.js +1 -1
- package/dist/esm/react/sdk/src/core/WalletIframe/client/router.js.map +1 -1
- package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js +2 -3
- package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js.map +1 -1
- package/dist/esm/react/sdk/src/core/defaultConfigs.js +3 -7
- package/dist/esm/react/sdk/src/core/defaultConfigs.js.map +1 -1
- package/dist/esm/react/sdk/src/core/nearCrypto.js +24 -6
- package/dist/esm/react/sdk/src/core/nearCrypto.js.map +1 -1
- package/dist/esm/react/sdk/src/core/rpcCalls.js +56 -26
- package/dist/esm/react/sdk/src/core/rpcCalls.js.map +1 -1
- package/dist/esm/react/sdk/src/core/types/emailRecovery.js +26 -0
- package/dist/esm/react/sdk/src/core/types/emailRecovery.js.map +1 -0
- package/dist/esm/react/styles/styles.css +58 -3
- package/dist/esm/sdk/{defaultConfigs-DpslkAQd.js → defaultConfigs-CfQDV-ya.js} +3 -7
- package/dist/esm/sdk/{getDeviceNumber-fXizNGQl.js → getDeviceNumber-BpernPnM.js} +4 -8
- package/dist/esm/sdk/getDeviceNumber-BpernPnM.js.map +1 -0
- package/dist/esm/sdk/offline-export-app.js +23 -6
- package/dist/esm/sdk/offline-export-app.js.map +1 -1
- package/dist/esm/sdk/{router-DuGYOd3G.js → router-BWtacLJg.js} +1 -1
- package/dist/esm/sdk/{rpcCalls-BQrJMTdg.js → rpcCalls-CYGJSCgm.js} +3 -3
- package/dist/esm/sdk/{rpcCalls-YVeUVMk2.js → rpcCalls-DZZSa-sk.js} +57 -27
- package/dist/esm/sdk/{transactions-bqaAwL4k.js → transactions-Cn9xTWlK.js} +2 -2
- package/dist/esm/sdk/{transactions-bqaAwL4k.js.map → transactions-Cn9xTWlK.js.map} +1 -1
- package/dist/esm/sdk/{transactions-BalIhtJ9.js → transactions-DfdwDQCn.js} +1 -1
- package/dist/esm/sdk/wallet-iframe-host.js +660 -590
- package/dist/esm/server/email-recovery/emailParsers.js +3 -1
- package/dist/esm/server/email-recovery/emailParsers.js.map +1 -1
- package/dist/esm/server/email-recovery/index.js +6 -6
- package/dist/esm/server/email-recovery/index.js.map +1 -1
- package/dist/esm/server/email-recovery/rpcCalls.js +22 -3
- package/dist/esm/server/email-recovery/rpcCalls.js.map +1 -1
- package/dist/esm/server/router/cloudflare.js +8 -3
- package/dist/esm/server/router/cloudflare.js.map +1 -1
- package/dist/esm/server/router/express.js.map +1 -1
- package/dist/esm/server/sdk/src/core/defaultConfigs.js +2 -4
- package/dist/esm/server/sdk/src/core/defaultConfigs.js.map +1 -1
- package/dist/esm/server/sdk/src/core/nearCrypto.js +26 -8
- package/dist/esm/server/sdk/src/core/nearCrypto.js.map +1 -1
- package/dist/esm/wasm_vrf_worker/pkg/wasm_vrf_worker_bg.wasm +0 -0
- package/dist/types/src/core/EmailRecovery/index.d.ts +8 -0
- package/dist/types/src/core/EmailRecovery/index.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/emailRecovery.d.ts +8 -5
- package/dist/types/src/core/TatchiPasskey/emailRecovery.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/index.d.ts +1 -1
- package/dist/types/src/core/TatchiPasskey/index.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/scanDevice.d.ts.map +1 -1
- package/dist/types/src/core/WalletIframe/TatchiPasskeyIframe.d.ts +1 -1
- package/dist/types/src/core/WalletIframe/TatchiPasskeyIframe.d.ts.map +1 -1
- package/dist/types/src/core/WalletIframe/client/router.d.ts +1 -1
- package/dist/types/src/core/WalletIframe/client/router.d.ts.map +1 -1
- package/dist/types/src/core/WalletIframe/shared/messages.d.ts +1 -1
- package/dist/types/src/core/WalletIframe/shared/messages.d.ts.map +1 -1
- package/dist/types/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.d.ts +2 -1
- package/dist/types/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.d.ts.map +1 -1
- package/dist/types/src/core/defaultConfigs.d.ts.map +1 -1
- package/dist/types/src/core/nearCrypto.d.ts +14 -0
- package/dist/types/src/core/nearCrypto.d.ts.map +1 -1
- package/dist/types/src/core/rpcCalls.d.ts +11 -8
- package/dist/types/src/core/rpcCalls.d.ts.map +1 -1
- package/dist/types/src/core/types/emailRecovery.d.ts +10 -0
- package/dist/types/src/core/types/emailRecovery.d.ts.map +1 -0
- package/dist/types/src/core/types/index.d.ts +1 -0
- package/dist/types/src/core/types/index.d.ts.map +1 -1
- package/dist/types/src/core/types/tatchi.d.ts +0 -4
- package/dist/types/src/core/types/tatchi.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/adapters/tatchi.d.ts +2 -0
- package/dist/types/src/react/components/PasskeyAuthMenu/adapters/tatchi.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/client.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/controller/useSDKEvents.d.ts +10 -0
- package/dist/types/src/react/components/PasskeyAuthMenu/controller/useSDKEvents.d.ts.map +1 -0
- package/dist/types/src/react/components/PasskeyAuthMenu/types.d.ts +8 -3
- package/dist/types/src/react/components/PasskeyAuthMenu/types.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/ui/ContentSwitcher.d.ts +2 -0
- package/dist/types/src/react/components/PasskeyAuthMenu/ui/ContentSwitcher.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.d.ts +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.d.ts.map +1 -1
- package/dist/types/src/react/context/useSDKFlowRuntime.d.ts +10 -0
- package/dist/types/src/react/context/useSDKFlowRuntime.d.ts.map +1 -0
- package/dist/types/src/react/context/useTatchiContextValue.d.ts.map +1 -1
- package/dist/types/src/react/context/useTatchiWithSdkFlow.d.ts +9 -0
- package/dist/types/src/react/context/useTatchiWithSdkFlow.d.ts.map +1 -0
- package/dist/types/src/react/types.d.ts +31 -0
- package/dist/types/src/react/types.d.ts.map +1 -1
- package/dist/types/src/server/email-recovery/emailParsers.d.ts.map +1 -1
- package/dist/types/src/server/email-recovery/index.d.ts +5 -6
- package/dist/types/src/server/email-recovery/index.d.ts.map +1 -1
- package/dist/types/src/server/email-recovery/rpcCalls.d.ts +1 -0
- package/dist/types/src/server/email-recovery/rpcCalls.d.ts.map +1 -1
- package/dist/types/src/server/router/cloudflare-adaptor.d.ts.map +1 -1
- package/dist/workers/wasm_vrf_worker_bg.wasm +0 -0
- package/package.json +1 -1
- package/dist/esm/sdk/getDeviceNumber-fXizNGQl.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudflare.js","names":["out: ShamirApplyServerLockResponse","e: any","out: ShamirRemoveServerLockResponse","normalizedHeaders: Record<string, string>","v","base: Logger","allowedOrigin: string | '*' | undefined","out: Record<string, string>","e: any","body: unknown","res","headersObj: Record<string, string>","rawBody: unknown","result","currentKeyId: string | null","shamirReady: boolean | null","shamirCurrentKeyId: string | null","shamirError: string | undefined","e: unknown"],"sources":["../../../../src/core/WalletIframe/validation.ts","../../../../src/server/core/shamirHandlers.ts","../../../../src/server/core/SessionService.ts","../../../../src/server/email-recovery/zkEmail/index.ts","../../../../src/server/email-recovery/emailParsers.ts","../../../../src/server/core/logger.ts","../../../../src/server/router/logger.ts","../../../../src/server/router/cloudflare-adaptor.ts"],"sourcesContent":["// Shared runtime validation helpers for Wallet Iframe code.\n\nexport function isObject(x: unknown): x is Record<string, unknown> {\n return x !== null && typeof x === 'object';\n}\n\nexport function isString(x: unknown): x is string {\n return typeof x === 'string';\n}\n\nexport function isNonEmptyString(x: unknown): x is string {\n return typeof x === 'string' && x.length > 0;\n}\n\nexport function isNumber(x: unknown): x is number {\n return typeof x === 'number';\n}\n\nexport function isFiniteNumber(x: unknown): x is number {\n return typeof x === 'number' && Number.isFinite(x);\n}\n\nexport function isFunction(x: unknown): x is Function {\n return typeof x === 'function';\n}\n\nexport function isBoolean(x: unknown): x is boolean {\n return typeof x === 'boolean';\n}\n\nexport function isArray<T = unknown>(x: unknown): x is T[] {\n return Array.isArray(x);\n}\n\n// ========= Assertions (throw on mismatch) =========\n\nexport function assertString(val: unknown, name = 'value'): string {\n if (typeof val !== 'string') throw new Error(`Invalid ${name}: expected string`);\n return val;\n}\n\nexport function assertNumber(val: unknown, name = 'value'): number {\n if (typeof val !== 'number' || !Number.isFinite(val)) throw new Error(`Invalid ${name}: expected finite number`);\n return val;\n}\n\nexport function assertBoolean(val: unknown, name = 'value'): boolean {\n if (typeof val !== 'boolean') throw new Error(`Invalid ${name}: expected boolean`);\n return val;\n}\n\nexport function assertObject<T extends Record<string, unknown> = Record<string, unknown>>(val: unknown, name = 'value'): T {\n if (!isObject(val)) throw new Error(`Invalid ${name}: expected object`);\n return val as T;\n}\n\nexport function assertArray<T = unknown>(val: unknown, name = 'value'): T[] {\n if (!Array.isArray(val)) throw new Error(`Invalid ${name}: expected array`);\n return val as T[];\n}\n\n// Shallowly remove function-valued properties (postMessage/clone safety)\nexport function stripFunctionsShallow<T extends Record<string, unknown>>(obj?: T): Partial<T> | undefined {\n if (!obj || !isObject(obj)) return undefined;\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (!isFunction(v)) out[k] = v as unknown;\n }\n return out as Partial<T>;\n}\n\n// ===============================\n// SignedTransaction shape helpers\n// ===============================\n\nexport interface PlainSignedTransactionLike {\n transaction: unknown;\n signature: unknown;\n borsh_bytes?: unknown;\n borshBytes?: unknown;\n base64Encode?: unknown;\n}\n\nexport function isPlainSignedTransactionLike(x: unknown): x is PlainSignedTransactionLike {\n if (!isObject(x)) return false;\n const hasTx = 'transaction' in x;\n const hasSig = 'signature' in x;\n const bytes = x as { borsh_bytes?: unknown; borshBytes?: unknown };\n const hasBytes = Array.isArray(bytes.borsh_bytes) || bytes.borshBytes instanceof Uint8Array;\n const hasMethod = typeof (x as { base64Encode?: unknown }).base64Encode === 'function';\n return hasTx && hasSig && hasBytes && !hasMethod;\n}\n\nexport function extractBorshBytesFromPlainSignedTx(x: PlainSignedTransactionLike): number[] {\n const asArray = Array.isArray(x.borsh_bytes) ? (x.borsh_bytes as number[]) : undefined;\n if (asArray) return asArray;\n const asU8 = (x.borshBytes instanceof Uint8Array) ? x.borshBytes : undefined;\n return Array.from(asU8 || new Uint8Array());\n}\n","import type {\n ShamirApplyServerLockRequest,\n ShamirApplyServerLockResponse,\n ShamirRemoveServerLockRequest,\n ShamirRemoveServerLockResponse,\n} from './types';\nimport type { ShamirService } from './ShamirService';\nimport { isString } from '../../core/WalletIframe/validation';\n\nexport async function handleApplyServerLock(\n service: ShamirService,\n request: { body?: { kek_c_b64u?: string } },\n): Promise<{ status: number; headers: Record<string, string>; body: string }> {\n try {\n const kek_c_b64u = request.body?.kek_c_b64u;\n if (!isString(kek_c_b64u) || !kek_c_b64u) {\n return {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'kek_c_b64u required and must be a non-empty string' }),\n };\n }\n\n const out: ShamirApplyServerLockResponse = await service.applyServerLock(kek_c_b64u);\n const keyId = service.getCurrentShamirKeyId();\n\n return {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ ...out, keyId }),\n };\n } catch (e: any) {\n return {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'internal', details: e?.message }),\n };\n }\n}\n\nexport async function handleRemoveServerLock(\n service: ShamirService,\n request: { body?: { kek_cs_b64u?: string; keyId?: string } },\n): Promise<{ status: number; headers: Record<string, string>; body: string }> {\n try {\n const body = request.body;\n if (!body) {\n return {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'Missing body' }),\n };\n }\n const { kek_cs_b64u, keyId } = body;\n if (!isString(kek_cs_b64u) || !kek_cs_b64u) {\n return {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'kek_cs_b64u required and must be a non-empty string' }),\n };\n }\n if (!isString(keyId) || !keyId) {\n return {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'keyId required and must be a non-empty string' }),\n };\n }\n\n const providedKeyId = String(keyId);\n const currentKeyId = service.getCurrentShamirKeyId();\n let out: ShamirRemoveServerLockResponse;\n\n if (currentKeyId && providedKeyId === currentKeyId) {\n out = await service.removeServerLock(kek_cs_b64u);\n } else if (service.hasGraceKey(providedKeyId)) {\n out = await service.removeGraceServerLockWithKey(providedKeyId, { kek_cs_b64u } as ShamirRemoveServerLockRequest);\n } else {\n return {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'unknown keyId' }),\n };\n }\n\n return {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(out),\n };\n } catch (e: any) {\n return {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'internal', details: e?.message }),\n };\n }\n}\n\nexport async function handleGetShamirKeyInfo(\n service: ShamirService,\n): Promise<{ status: number; headers: Record<string, string>; body: string }> {\n try {\n await service.ensureReady();\n const currentKeyId = service.getCurrentShamirKeyId();\n const graceKeyIds = service.getGraceKeyIds();\n\n return {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n currentKeyId,\n p_b64u: service.getShamirConfig()?.shamir_p_b64u ?? null,\n graceKeyIds,\n }),\n };\n } catch (e: any) {\n return {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'internal', details: e?.message }),\n };\n }\n}\n\nexport async function handleListGraceKeys(\n service: ShamirService,\n): Promise<{ status: number; headers: Record<string, string>; body: string }> {\n try {\n await service.ensureGraceKeysLoaded();\n return {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ graceKeyIds: service.getGraceKeyIds() }),\n };\n } catch (e: any) {\n return {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'internal', details: e?.message }),\n };\n }\n}\n\nexport async function handleAddGraceKey(\n service: ShamirService,\n request: { e_s_b64u?: string; d_s_b64u?: string },\n): Promise<{ status: number; headers: Record<string, string>; body: string }> {\n try {\n const { e_s_b64u, d_s_b64u } = request || ({} as any);\n if (!isString(e_s_b64u) || !e_s_b64u || !isString(d_s_b64u) || !d_s_b64u) {\n return {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'e_s_b64u and d_s_b64u required' }),\n };\n }\n\n await service.ensureGraceKeysLoaded();\n const added = await service.addGraceKeyInternal({ e_s_b64u, d_s_b64u }, { persist: true, skipIfExists: true });\n if (!added) {\n return {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'failed to add grace key' }),\n };\n }\n\n return {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ keyId: added.keyId }),\n };\n } catch (e: any) {\n return {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'internal', details: e?.message }),\n };\n }\n}\n\nexport async function handleRemoveGraceKey(\n service: ShamirService,\n request: { keyId?: string },\n): Promise<{ status: number; headers: Record<string, string>; body: string }> {\n try {\n const keyId = request?.keyId;\n if (!isString(keyId) || !keyId) {\n return {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'keyId required and must be a non-empty string' }),\n };\n }\n\n const removed = await service.removeGraceKeyInternal(keyId, { persist: true });\n return {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ removed }),\n };\n } catch (e: any) {\n return {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'internal', details: e?.message }),\n };\n }\n}\n","\nexport interface SessionConfig {\n jwt?: {\n /** Required: JWT signing hook; return a complete token */\n signToken?: (input: { header: Record<string, unknown>; payload: Record<string, unknown> }) => Promise<string> | string;\n /** Required: JWT verification hook */\n verifyToken?: (token: string) => Promise<{ valid: boolean; payload?: any }> | { valid: boolean; payload?: any };\n /** Optional: sliding refresh window (seconds) to allow /session/refresh before exp, default 900 (15 min) */\n refreshWindowSec?: number;\n /** Optional: build additional claims to include in the payload */\n buildClaims?: (input: { sub: string; context?: Record<string, unknown> }) => Promise<Record<string, unknown> | void> | Record<string, unknown> | void;\n };\n cookie?: {\n /** Cookie name. Default: 'w3a_session' */\n name?: string;\n /** Optional override: build Set-Cookie header for a new token */\n buildSetHeader?: (token: string) => string;\n /** Optional override: build Set-Cookie header that clears the cookie */\n buildClearHeader?: () => string;\n /** Optional override: extract token from headers (Authorization/Cookie) */\n extractToken?: (headers: Record<string, string | string[] | undefined>, cookieName: string) => string | null;\n };\n}\n\nexport class SessionService<TClaims = any> {\n private cfg: NonNullable<SessionConfig>;\n\n constructor(cfg: NonNullable<SessionConfig>) {\n this.cfg = cfg || ({} as any);\n }\n\n getCookieName(): string {\n return this.cfg?.cookie?.name || 'w3a_session';\n }\n\n buildSetCookie(token: string): string {\n if (this.cfg?.cookie?.buildSetHeader) return this.cfg.cookie.buildSetHeader(token);\n const name = this.getCookieName();\n const cookieParts = [`${name}=${token}`];\n const path = '/';\n const httpOnly = true;\n const secure = true; // default secure\n const sameSite = 'Lax';\n const maxAge = 24 * 3600; // 1 day default\n cookieParts.push(`Path=${path}`);\n if (httpOnly) cookieParts.push('HttpOnly');\n if (secure) cookieParts.push('Secure');\n if (sameSite) cookieParts.push(`SameSite=${sameSite}`);\n if (maxAge) {\n cookieParts.push(`Max-Age=${maxAge}`);\n const expires = new Date(Date.now() + (maxAge * 1000)).toUTCString();\n cookieParts.push(`Expires=${expires}`);\n }\n return cookieParts.join('; ');\n }\n\n buildClearCookie(): string {\n if (this.cfg?.cookie?.buildClearHeader) return this.cfg.cookie.buildClearHeader();\n const name = this.getCookieName();\n const path = '/';\n const secure = true;\n const httpOnly = true;\n const parts = [\n `${name}=`,\n `Path=${path}`,\n 'Max-Age=0',\n 'Expires=Thu, 01 Jan 1970 00:00:00 GMT'\n ];\n if (httpOnly) parts.push('HttpOnly');\n if (secure) parts.push('Secure');\n const sameSite = 'Lax';\n if (sameSite) parts.push(`SameSite=${sameSite}`);\n return parts.join('; ');\n }\n\n /** Sign a JWT with configured algorithm. Adds iat/exp and copies iss/aud. */\n async signJwt(sub: string, extraClaims?: Record<string, unknown>): Promise<string> {\n const jwt = this.cfg?.jwt || {};\n const built = await Promise.resolve(jwt.buildClaims?.({ sub, context: extraClaims })) || {};\n const payload = { sub, ...(extraClaims || {}), ...(built || {}) } as Record<string, unknown>;\n if (typeof jwt.signToken === 'function') {\n // Full override of signing: user supplies the complete token\n const token = await Promise.resolve(jwt.signToken({ header: { typ: 'JWT' }, payload } as any));\n return token;\n }\n throw new Error('SessionService: No JWT signing hook or provider configured');\n }\n\n /** Verify signature and expiration. Returns payload on success. */\n async verifyJwt(token: string): Promise<{ valid: boolean; payload?: any }> {\n const verify = this.cfg?.jwt?.verifyToken;\n if (typeof verify !== 'function') return { valid: false };\n return await Promise.resolve(verify(token));\n }\n\n parse(headers: Record<string, string | string[] | undefined>): Promise<{ ok: boolean; claims?: TClaims } | { ok: false }>{\n const authHeader = (headers['authorization'] || headers['Authorization']) as string | undefined;\n let token: string | null = null;\n if (authHeader && /^Bearer\\s+/.test(authHeader)) token = authHeader.replace(/^Bearer\\s+/i, '').trim();\n const cookieHeader = (headers['cookie'] || headers['Cookie']) as string | undefined;\n if (!token && cookieHeader) {\n const name = this.getCookieName();\n for (const part of cookieHeader.split(';')) {\n const [k, v] = part.split('=');\n if (k && k.trim() === name) { token = (v || '').trim(); break; }\n }\n }\n if (!token) return Promise.resolve({ ok: false });\n return this.verifyJwt(token).then(v => v.valid ? { ok: true, claims: v.payload as TClaims } : { ok: false });\n }\n\n // === token helpers ===\n extractTokenFromHeaders(headers: Record<string, string | string[] | undefined>): string | null {\n if (this.cfg?.cookie?.extractToken) return this.cfg.cookie.extractToken(headers, this.getCookieName());\n const authHeader = (headers['authorization'] || headers['Authorization']) as string | undefined;\n if (authHeader && /^Bearer\\s+/.test(authHeader)) return authHeader.replace(/^Bearer\\s+/i, '').trim();\n const cookieHeader = (headers['cookie'] || headers['Cookie']) as string | undefined;\n if (cookieHeader) {\n const name = this.getCookieName();\n for (const part of cookieHeader.split(';')) {\n const [k, v] = part.split('=');\n if (k && k.trim() === name) return (v || '').trim();\n }\n }\n return null;\n }\n\n async refresh(headers: Record<string, string | string[] | undefined>): Promise<{ ok: boolean; jwt?: string; code?: string; message?: string }>{\n try {\n const token = this.extractTokenFromHeaders(headers);\n if (!token) return { ok: false, code: 'unauthorized', message: 'No session token' };\n const v = await this.verifyJwt(token);\n if (!v.valid) return { ok: false, code: 'unauthorized', message: 'Invalid token' };\n const payload: any = v.payload || {};\n if (!this.isWithinRefreshWindow(payload)) return { ok: false, code: 'not_eligible', message: 'Not within refresh window' };\n const sub = String(payload.sub || '');\n if (!sub) return { ok: false, code: 'invalid_claims', message: 'Missing sub claim' };\n const next = await this.signJwt(sub);\n return { ok: true, jwt: next };\n } catch (e: any) {\n return { ok: false, code: 'internal', message: e?.message || 'Refresh failed' };\n }\n }\n\n nowSeconds(): number { return Math.floor(Date.now() / 1000); }\n\n private isWithinRefreshWindow(payload: any): boolean {\n try {\n const now = this.nowSeconds();\n const exp = Number(payload?.exp || 0);\n if (!exp || now >= exp) return false; // no refresh if already expired\n const windowSec = Number(this.cfg?.jwt?.refreshWindowSec || 15 * 60);\n return (exp - now) <= windowSec;\n } catch { return false; }\n }\n}\n\n/*\n * Utility: parse comma-separated list of origins into a normalized unique list\n * - canonicalizes to protocol + host + optional port\n * - lowercases host, strips path/query/hash, trims spaces/trailing slashes\n */\nexport function parseCsvList(input?: string): string[] {\n const out = new Set<string>();\n for (const raw of String(input || '').split(',')) {\n const s = raw.trim();\n if (!s) continue;\n try {\n const u = new URL(s);\n const host = u.hostname.toLowerCase();\n const port = u.port ? `:${u.port}` : '';\n const proto = u.protocol === 'http:' || u.protocol === 'https:' ? u.protocol : 'https:';\n out.add(`${proto}//${host}${port}`);\n } catch {\n const stripped = s.replace(/\\/$/, '');\n if (stripped) out.add(stripped);\n }\n }\n return Array.from(out);\n}\n\n/*\n * Utility: merge multiple CSV lists of origins and return normalized list or '*'\n */\nexport function buildCorsOrigins(...inputs: Array<string | undefined>): string[] | '*' {\n const merged = new Set<string>();\n for (const input of inputs) {\n for (const origin of parseCsvList(input)) merged.add(origin);\n }\n const list = Array.from(merged);\n return list.length > 0 ? list : '*';\n}\n","import { ZkEmailProverClient, type ZkEmailProverClientOptions, type ZkEmailProverError } from './proverClient';\n\nexport { ZkEmailProverClient } from './proverClient';\nexport type { ZkEmailProverClientOptions, ZkEmailProverError, ZkEmailProverResponse } from './proverClient';\n\nexport interface ForwardableEmailPayload {\n from: string;\n to: string;\n headers: Record<string, string>;\n raw?: string;\n rawSize?: number;\n}\n\nexport type NormalizedEmailResult =\n | { ok: true; payload: ForwardableEmailPayload }\n | { ok: false; code: string; message: string };\n\nexport interface GenerateZkEmailProofResult {\n proof: unknown;\n publicInputs: string[];\n}\n\nexport interface ParsedZkEmailBindings {\n accountId: string;\n newPublicKey: string;\n fromEmail: string;\n timestamp: string;\n requestId: string;\n}\n\n/**\n * Build a minimal ForwardableEmailPayload from a raw RFC822 email string.\n * This is primarily used by server-side helpers that receive only a raw\n * email blob (no pre-normalized headers).\n */\nexport function buildForwardablePayloadFromRawEmail(raw: string): ForwardableEmailPayload {\n const safeRaw = typeof raw === 'string' ? raw : '';\n const lines = safeRaw.split(/\\r?\\n/);\n\n const getHeader = (name: string): string | undefined => {\n const line = lines.find(l => new RegExp(`^${name}:`, 'i').test(l));\n if (!line) return undefined;\n const idx = line.indexOf(':');\n const rest = idx >= 0 ? line.slice(idx + 1) : '';\n const value = rest.trim();\n return value || undefined;\n };\n\n const fromHeader = getHeader('from') || 'unknown@zkemail.local';\n const toHeader = getHeader('to') || 'recover@zkemail.local';\n\n const headers: Record<string, string> = {};\n const subjectHeader = getHeader('subject');\n const dateHeader = getHeader('date');\n\n if (fromHeader) headers.from = fromHeader;\n if (toHeader) headers.to = toHeader;\n if (subjectHeader) headers.subject = subjectHeader;\n if (dateHeader) headers.date = dateHeader;\n\n return {\n from: fromHeader,\n to: toHeader,\n headers,\n raw: safeRaw,\n rawSize: safeRaw.length,\n };\n}\n\nexport function normalizeForwardableEmailPayload(input: unknown): NormalizedEmailResult {\n if (!input || typeof input !== 'object') {\n return { ok: false, code: 'invalid_email', message: 'JSON body required' };\n }\n\n const body = input as Partial<ForwardableEmailPayload>;\n const { from, to, headers, raw, rawSize } = body;\n\n if (!from || typeof from !== 'string' || !to || typeof to !== 'string') {\n return { ok: false, code: 'invalid_email', message: 'from and to are required' };\n }\n\n if (!headers || typeof headers !== 'object') {\n return { ok: false, code: 'invalid_email', message: 'headers object is required' };\n }\n\n const normalizedHeaders: Record<string, string> = {};\n for (const [k, v] of Object.entries(headers as Record<string, unknown>)) {\n normalizedHeaders[String(k).toLowerCase()] = String(v);\n }\n\n return {\n ok: true,\n payload: {\n from,\n to,\n headers: normalizedHeaders,\n raw: typeof raw === 'string' ? raw : undefined,\n rawSize: typeof rawSize === 'number' ? rawSize : undefined,\n },\n };\n}\n\n/**\n * Parse NEAR accountId from the Subject line inside a raw RFC822 email.\n *\n * Expected format (case-insensitive on \"Subject\" and \"recover\"):\n * Subject: recover-123ABC bob.testnet ed25519:<pk>\n *\n * Returns the parsed accountId (e.g. \"bob.testnet\") or null if not found.\n */\nexport function parseAccountIdFromSubject(raw: string | undefined | null): string | null {\n if (!raw || typeof raw !== 'string') return null;\n\n // Accept either a full RFC822 message (with \"Subject: ...\" header)\n // or a bare Subject value (\"recover-123ABC bob.testnet ed25519:<pk>\").\n let subjectText = '';\n const lines = raw.split(/\\r?\\n/);\n const subjectLine = lines.find(line => /^subject:/i.test(line));\n if (subjectLine) {\n const idx = subjectLine.indexOf(':');\n const restRaw = idx >= 0 ? subjectLine.slice(idx + 1) : '';\n subjectText = restRaw.trim();\n } else {\n subjectText = raw.trim();\n }\n\n if (!subjectText) return null;\n\n // Strip common reply/forward prefixes\n subjectText = subjectText.replace(/^(re|fwd):\\s*/i, '').trim();\n if (!subjectText) return null;\n\n // Strict format: \"recover-<request_id> <accountId> [ed25519:<pk>]\"\n const match = subjectText.match(\n /^recover-([A-Za-z0-9]{6})\\s+([^\\s]+)(?:\\s+ed25519:[^\\s]+)?\\s*$/i\n );\n if (match?.[2]) {\n return match[2];\n }\n\n return null;\n}\n\nfunction parseSubjectBindings(\n rawSubject: string | undefined | null\n): { accountId: string; newPublicKey: string; requestId: string } | null {\n if (!rawSubject || typeof rawSubject !== 'string') return null;\n\n const lines = rawSubject.split(/\\r?\\n/);\n let subjectText = '';\n const subjectLine = lines.find(line => /^subject:/i.test(line));\n if (subjectLine) {\n const idx = subjectLine.indexOf(':');\n const restRaw = idx >= 0 ? subjectLine.slice(idx + 1) : '';\n subjectText = restRaw.trim();\n } else {\n subjectText = rawSubject.trim();\n }\n if (!subjectText) return null;\n\n subjectText = subjectText.replace(/^(re|fwd):\\s*/i, '').trim();\n if (!subjectText) return null;\n\n // Strict format:\n // \"recover-<request_id> <accountId> ed25519:<pk>\"\n const match = subjectText.match(\n /^recover-([A-Za-z0-9]{6})\\s+([^\\s]+)\\s+ed25519:([^\\s]+)\\s*$/i\n );\n if (!match) return null;\n\n const [, requestId, accountId, newPublicKey] = match;\n\n return {\n accountId,\n newPublicKey,\n requestId,\n };\n}\n\nexport function extractZkEmailBindingsFromPayload(\n payload: ForwardableEmailPayload\n): ParsedZkEmailBindings | null {\n const raw = payload.raw || '';\n const lines = raw.split(/\\r?\\n/);\n\n const subjectLine = lines.find(line => /^subject:/i.test(line));\n const subjectBindings = parseSubjectBindings(subjectLine ?? '');\n if (!subjectBindings) {\n return null;\n }\n\n const headers = payload.headers || {};\n let fromEmailRaw: string | undefined =\n (headers['from'] as any) ||\n (headers['x-from-email'] as any);\n let dateRaw: string | undefined =\n (headers['date'] as any) ||\n (headers['x-original-date'] as any);\n\n // Fallback: if headers object does not contain from/date,\n // attempt to parse them from the raw RFC822 email lines.\n if (!fromEmailRaw || !dateRaw) {\n for (const line of lines) {\n if (!fromEmailRaw && /^from:/i.test(line)) {\n const idx = line.indexOf(':');\n fromEmailRaw = idx >= 0 ? line.slice(idx + 1).trim() : '';\n }\n if (!dateRaw && /^date:/i.test(line)) {\n const idx = line.indexOf(':');\n dateRaw = idx >= 0 ? line.slice(idx + 1).trim() : '';\n }\n if (fromEmailRaw && dateRaw) break;\n }\n }\n\n const fromEmail = String(fromEmailRaw || '').trim();\n const timestamp = String(dateRaw || '').trim();\n\n if (!fromEmail || !timestamp) {\n return null;\n }\n\n return {\n accountId: subjectBindings.accountId,\n newPublicKey: subjectBindings.newPublicKey,\n fromEmail,\n timestamp,\n requestId: subjectBindings.requestId,\n };\n}\n\nexport async function generateZkEmailProofFromPayload(\n payload: ForwardableEmailPayload,\n prover: ZkEmailProverClientOptions | ZkEmailProverClient\n): Promise<GenerateZkEmailProofResult> {\n if (!payload.raw || typeof payload.raw !== 'string') {\n const err: ZkEmailProverError = Object.assign(\n new Error('raw email contents are required to generate a zk-email proof'),\n { code: 'missing_raw_email' }\n );\n throw err;\n }\n\n const client =\n (prover && typeof (prover as any).proveEmail === 'function')\n ? (prover as ZkEmailProverClient)\n : new ZkEmailProverClient(prover as ZkEmailProverClientOptions);\n\n const res = await client.proveEmail(payload.raw);\n\n return {\n proof: res.proof,\n publicInputs: res.publicSignals,\n };\n}\n","import type { EmailRecoveryMode } from './types';\nimport { normalizeForwardableEmailPayload, parseAccountIdFromSubject } from './zkEmail';\n\nexport enum EmailRecoveryModeHint {\n ZkEmail = 'zk-email',\n TeeEncrypted = 'tee-encrypted',\n OnchainPublic = 'onchain-public',\n}\n\nexport function normalizeRecoveryMode(raw: string | undefined | null): EmailRecoveryMode | null {\n if (!raw) return null;\n const value = raw.trim().toLowerCase();\n if (value === EmailRecoveryModeHint.ZkEmail) return 'zk-email';\n if (value === EmailRecoveryModeHint.TeeEncrypted) return 'tee-encrypted';\n if (value === EmailRecoveryModeHint.OnchainPublic) return 'onchain-public';\n return null;\n}\n\nexport function extractRecoveryModeFromBody(emailBlob?: string): EmailRecoveryMode | null {\n if (!emailBlob) return null;\n\n const lines = emailBlob.split(/\\r?\\n/);\n const bodyStartIndex = lines.findIndex(line => line.trim() === '');\n if (bodyStartIndex === -1) return null;\n\n const bodyLines = lines.slice(bodyStartIndex + 1);\n const firstNonEmptyBodyLine = bodyLines.find(line => line.trim() !== '');\n if (!firstNonEmptyBodyLine) return null;\n\n const candidate = firstNonEmptyBodyLine.trim();\n const normalized = normalizeRecoveryMode(candidate);\n if (normalized) return normalized;\n\n const lower = candidate.toLowerCase();\n if (lower.includes(EmailRecoveryModeHint.ZkEmail)) return 'zk-email';\n if (lower.includes(EmailRecoveryModeHint.TeeEncrypted)) return 'tee-encrypted';\n if (lower.includes(EmailRecoveryModeHint.OnchainPublic)) return 'onchain-public';\n\n return null;\n}\n\ntype HeaderValue = string | string[] | undefined;\ntype HeadersLike = Headers | Record<string, HeaderValue> | undefined;\n\nexport type RecoverEmailParseResult =\n | { ok: true; accountId: string; emailBlob: string; explicitMode?: string }\n | { ok: false; status: number; code: string; message: string };\n\nfunction getHeader(headers: HeadersLike, name: string): string | undefined {\n if (!headers) return undefined;\n\n const maybeHeaders = headers as any;\n if (typeof maybeHeaders.get === 'function') {\n const v = maybeHeaders.get(name);\n return (typeof v === 'string') ? v : undefined;\n }\n\n const record = headers as Record<string, HeaderValue>;\n const v = record[name.toLowerCase()] ?? record[name];\n if (Array.isArray(v)) return (typeof v[0] === 'string') ? v[0] : undefined;\n return (typeof v === 'string') ? v : undefined;\n}\n\nfunction parseExplicitMode(body: unknown, headers?: HeadersLike): string | undefined {\n const modeFromBody =\n (typeof (body as any)?.explicitMode === 'string' ? String((body as any).explicitMode) : '') ||\n (typeof (body as any)?.explicit_mode === 'string' ? String((body as any).explicit_mode) : '');\n const modeFromHeader = getHeader(headers, 'x-email-recovery-mode') || getHeader(headers, 'x-recovery-mode') || '';\n const raw = (modeFromBody || modeFromHeader).trim();\n return raw ? raw : undefined;\n}\n\nexport function parseRecoverEmailRequest(body: unknown, opts: { headers?: HeadersLike } = {}): RecoverEmailParseResult {\n const explicitMode = parseExplicitMode(body, opts.headers);\n\n const normalized = normalizeForwardableEmailPayload(body);\n if (!normalized.ok) {\n return { ok: false, status: 400, code: normalized.code, message: normalized.message };\n }\n\n const payload = normalized.payload;\n const emailBlob = payload.raw || '';\n const emailHeaders = payload.headers || {};\n\n const subjectHeader = emailHeaders['subject'];\n const parsedAccountId = parseAccountIdFromSubject(subjectHeader || emailBlob);\n const headerAccountId = String(emailHeaders['x-near-account-id'] || emailHeaders['x-account-id'] || '').trim();\n const accountId = (parsedAccountId || headerAccountId || '').trim();\n\n if (!accountId) {\n return { ok: false, status: 400, code: 'missing_account', message: 'x-near-account-id header is required' };\n }\n if (!emailBlob) {\n return { ok: false, status: 400, code: 'missing_email', message: 'raw email blob is required' };\n }\n\n return { ok: true, accountId, emailBlob, explicitMode };\n}\n\nconst EMAIL_ADDRESS_REGEX =\n /([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*)/;\n\nexport function canonicalizeEmail(input: string): string {\n const raw = String(input || '').trim();\n if (!raw) return '';\n\n // Handle cases where a full header line is passed in (e.g. \"From: ...\").\n const withoutHeaderName = raw.replace(/^[a-z0-9-]+\\s*:\\s*/i, '').trim();\n\n // Prefer the common \"Name <email@domain>\" format when present, but still\n // validate/extract the actual address via regex.\n const angleMatch = withoutHeaderName.match(/<([^>]+)>/);\n const candidates = [\n angleMatch?.[1],\n withoutHeaderName,\n ].filter((v): v is string => typeof v === 'string' && v.length > 0);\n\n for (const candidate of candidates) {\n const cleaned = candidate.replace(/^mailto:\\s*/i, '');\n const match = cleaned.match(EMAIL_ADDRESS_REGEX);\n if (match?.[1]) {\n return match[1].trim().toLowerCase();\n }\n }\n\n return withoutHeaderName.toLowerCase();\n}\n\nexport function parseHeaderValue(rawEmail: string, name: string): string | undefined {\n try {\n const raw = String(rawEmail || '');\n if (!raw) return undefined;\n\n const lines = raw.split(/\\r?\\n/);\n const headerLines: string[] = [];\n\n // Only consider the header section (until the first blank line).\n for (const line of lines) {\n if (line.trim() === '') break;\n\n // RFC822 header folding: lines starting with whitespace continue previous header.\n if (/^\\s/.test(line) && headerLines.length > 0) {\n headerLines[headerLines.length - 1] += ` ${line.trim()}`;\n continue;\n }\n\n headerLines.push(line);\n }\n\n const headerName = name.trim();\n if (!headerName) return undefined;\n\n const re = new RegExp(`^${headerName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\s*:`, 'i');\n const found = headerLines.find((l) => re.test(l));\n if (!found) return undefined;\n\n const idx = found.indexOf(':');\n const value = idx >= 0 ? found.slice(idx + 1).trim() : '';\n return value || undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function parseRecoverSubjectBindings(\n rawEmail: string\n): { requestId: string; accountId: string; newPublicKey: string } | null {\n // Accept either a full RFC822 email or a bare Subject value.\n let subjectText = (parseHeaderValue(rawEmail, 'subject') || String(rawEmail || '')).trim();\n if (!subjectText) return null;\n\n // Strip common reply/forward prefixes.\n subjectText = subjectText.replace(/^(re|fwd):\\s*/i, '').trim();\n if (!subjectText) return null;\n\n // Strict format:\n // \"recover-<request_id> <accountId> ed25519:<pk>\"\n const match = subjectText.match(\n /^recover-([A-Za-z0-9]{6})\\s+([^\\s]+)\\s+ed25519:([^\\s]+)\\s*$/i\n );\n if (!match) return null;\n\n const [, requestId, accountId, newPublicKey] = match;\n return { requestId, accountId, newPublicKey };\n}\n","export type Logger = {\n debug?: (...args: unknown[]) => void;\n info?: (...args: unknown[]) => void;\n warn?: (...args: unknown[]) => void;\n error?: (...args: unknown[]) => void;\n log?: (...args: unknown[]) => void;\n};\n\nexport type NormalizedLogger = {\n debug: (...args: unknown[]) => void;\n info: (...args: unknown[]) => void;\n warn: (...args: unknown[]) => void;\n error: (...args: unknown[]) => void;\n};\n\nfunction safe(fn?: (...args: unknown[]) => void): (...args: unknown[]) => void {\n if (!fn) return () => {};\n return (...args: unknown[]) => {\n try {\n fn(...args);\n } catch {\n // Never allow logging to break request handling.\n }\n };\n}\n\n/**\n * Library code should never call `console.*` directly; the host decides where logs go.\n * - Default: no logs\n * - To enable: pass `logger: console` (or a structured logger) to the host config.\n */\nexport function normalizeLogger(logger?: Logger | null): NormalizedLogger {\n if (!logger) {\n return { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} };\n }\n\n const base: Logger = logger;\n const log = typeof base.log === 'function' ? base.log.bind(base) : undefined;\n\n const debug = (typeof base.debug === 'function' ? base.debug.bind(base) : log);\n const info = (typeof base.info === 'function' ? base.info.bind(base) : log);\n const warn = (typeof base.warn === 'function' ? base.warn.bind(base) : log);\n const error = (typeof base.error === 'function' ? base.error.bind(base) : log);\n\n return {\n debug: safe(debug),\n info: safe(info),\n warn: safe(warn),\n error: safe(error),\n };\n}\n\n","import type { Logger, NormalizedLogger } from '../core/logger';\nimport { normalizeLogger } from '../core/logger';\n\nexport type RouterLogger = Logger;\nexport type NormalizedRouterLogger = NormalizedLogger;\nexport const normalizeRouterLogger = normalizeLogger;\n","import type { AuthService } from '../core/AuthService';\nimport {\n handleApplyServerLock,\n handleRemoveServerLock,\n handleGetShamirKeyInfo,\n} from '../core/shamirHandlers';\nimport { buildCorsOrigins } from '../core/SessionService';\nimport type { SessionAdapter } from './express-adaptor';\nimport type { CreateAccountAndRegisterRequest } from '../core/types';\nimport { parseRecoverEmailRequest } from '../email-recovery/emailParsers';\nimport type { DelegateActionPolicy } from '../delegateAction';\nimport type { RouterLogger } from './logger';\nimport { normalizeRouterLogger } from './logger';\n\nexport interface RelayRouterOptions {\n healthz?: boolean;\n readyz?: boolean;\n // Optional list(s) of CORS origins (CSV strings or literal origins).\n // Pass raw strings; the router normalizes/merges internally.\n corsOrigins?: Array<string | undefined>;\n /**\n * Optional route for submitting NEP-461 SignedDelegate meta-transactions.\n *\n * - When omitted: disabled.\n * - When set: enabled at `route`.\n *\n * `policy` is server-controlled and is never read from the request body.\n */\n signedDelegate?: {\n route: string;\n policy?: DelegateActionPolicy;\n };\n // Optional: customize session route paths\n sessionRoutes?: { auth?: string; logout?: string };\n // Optional: pluggable session adapter\n session?: SessionAdapter | null;\n // Optional logger; defaults to silent.\n logger?: RouterLogger | null;\n}\n\n// Minimal Worker runtime types (avoid adding @cloudflare/workers-types dependency here)\nexport interface CfEnv {\n // Optional env overrides for `/.well-known/webauthn` (ROR origins list).\n //\n // Note: Do not add an index signature here. Cloudflare env bindings can include\n // KV namespaces, Durable Objects, etc., and requiring `[key: string]: string`\n // makes real-world `Env` types not assignable.\n ROR_CONTRACT_ID?: string;\n WEBAUTHN_CONTRACT_ID?: string;\n ROR_METHOD?: string;\n}\n\n/**\n * Convenience env shape matching the `examples/relay-cloudflare-worker` configuration.\n * This is optional — you can define your own `Env` type with different binding names.\n */\nexport interface RelayCloudflareWorkerEnv {\n RELAYER_ACCOUNT_ID: string;\n RELAYER_PRIVATE_KEY: string;\n // Optional overrides (SDK provides defaults when omitted)\n NEAR_RPC_URL?: string;\n NETWORK_ID?: string;\n WEBAUTHN_CONTRACT_ID: string;\n ACCOUNT_INITIAL_BALANCE?: string;\n CREATE_ACCOUNT_AND_REGISTER_GAS?: string;\n ZK_EMAIL_PROVER_BASE_URL?: string;\n ZK_EMAIL_PROVER_TIMEOUT_MS?: string;\n SHAMIR_P_B64U: string;\n SHAMIR_E_S_B64U: string;\n SHAMIR_D_S_B64U: string;\n EXPECTED_ORIGIN?: string;\n EXPECTED_WALLET_ORIGIN?: string;\n ENABLE_ROTATION?: string;\n RECOVER_EMAIL_RECIPIENT?: string;\n}\n\nexport interface CfExecutionContext {\n waitUntil(promise: Promise<unknown>): void;\n passThroughOnException(): void\n}\nexport interface CfScheduledEvent {\n scheduledTime?: number;\n cron?: string\n}\nexport interface CfEmailMessage {\n from: string;\n to: string;\n // Cloudflare uses `Headers`, but keep this flexible for userland tests.\n headers: Headers | Iterable<[string, string]> | Record<string, string>;\n raw: ReadableStream | ArrayBuffer | string;\n rawSize?: number;\n setReject(reason: string): void;\n}\n\nexport type FetchHandler = (request: Request, env?: CfEnv, ctx?: CfExecutionContext) => Promise<Response>;\nexport type ScheduledHandler = (event: CfScheduledEvent, env?: CfEnv, ctx?: CfExecutionContext) => Promise<void>;\nexport type EmailHandler = (message: CfEmailMessage, env?: CfEnv, ctx?: CfExecutionContext) => Promise<void>;\n\nfunction normalizePath(path: string): string {\n const trimmed = String(path || '').trim();\n if (!trimmed) return '';\n return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;\n}\n\nfunction json(body: unknown, init?: ResponseInit, extraHeaders?: Record<string, string>): Response {\n const headers = new Headers({ 'Content-Type': 'application/json; charset=utf-8' });\n\n // Merge init.headers into our base headers (ResponseInit headers are otherwise overwritten).\n const initHeaders = (init as any)?.headers as HeadersInit | undefined;\n if (initHeaders) {\n try {\n new Headers(initHeaders).forEach((v, k) => headers.set(k, v));\n } catch { }\n }\n\n if (extraHeaders) {\n for (const [k, v] of Object.entries(extraHeaders)) headers.set(k, v);\n }\n\n const { headers: _omit, ...rest } = init || {};\n return new Response(JSON.stringify(body), { status: 200, ...rest, headers });\n}\n\nfunction withCors(headers: Headers, opts?: RelayRouterOptions, request?: Request): void {\n if (!opts?.corsOrigins) return;\n let allowedOrigin: string | '*' | undefined;\n const normalized = buildCorsOrigins(...(opts.corsOrigins || []));\n if (normalized === '*') {\n allowedOrigin = '*';\n headers.set('Access-Control-Allow-Origin', '*');\n } else if (Array.isArray(normalized)) {\n const origin = request?.headers.get('Origin') || '';\n if (origin && normalized.includes(origin)) {\n allowedOrigin = origin;\n headers.set('Access-Control-Allow-Origin', origin);\n headers.append('Vary', 'Origin');\n }\n }\n headers.set('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');\n headers.set('Access-Control-Allow-Headers', 'Content-Type,Authorization');\n // Only advertise credentials when we echo back a specific origin (not '*')\n if (allowedOrigin && allowedOrigin !== '*') {\n headers.set('Access-Control-Allow-Credentials', 'true');\n }\n}\n\nfunction normalizeEmailAddress(input: string): string {\n const trimmed = String(input || '').trim();\n const angleStart = trimmed.indexOf('<');\n const angleEnd = trimmed.indexOf('>');\n if (angleStart !== -1 && angleEnd > angleStart) {\n return trimmed.slice(angleStart + 1, angleEnd).trim().toLowerCase();\n }\n return trimmed.toLowerCase();\n}\n\nfunction toLowercaseHeaderRecord(input: unknown): Record<string, string> {\n const out: Record<string, string> = {};\n if (!input) return out;\n\n const maybeHeaders = input as any;\n if (typeof maybeHeaders.forEach === 'function') {\n try {\n maybeHeaders.forEach((v: unknown, k: unknown) => {\n out[String(k).toLowerCase()] = String(v);\n });\n return out;\n } catch { }\n }\n\n if (typeof maybeHeaders[Symbol.iterator] === 'function') {\n try {\n for (const entry of maybeHeaders as Iterable<unknown>) {\n if (!Array.isArray(entry)) continue;\n const [k, v] = entry as any[];\n out[String(k).toLowerCase()] = String(v);\n }\n return out;\n } catch { }\n }\n\n if (typeof input === 'object') {\n for (const [k, v] of Object.entries(input as Record<string, unknown>)) {\n out[String(k).toLowerCase()] = String(v);\n }\n }\n\n return out;\n}\n\nasync function buildForwardableEmailPayloadFromCloudflareMessage(message: CfEmailMessage): Promise<{\n from: string;\n to: string;\n headers: Record<string, string>;\n raw: string;\n rawSize?: number;\n}> {\n const from = String(message?.from || '');\n const to = String(message?.to || '');\n const headers = toLowercaseHeaderRecord((message as any)?.headers);\n\n let raw = '';\n try {\n raw = await new Response((message as any)?.raw).text();\n } catch { }\n\n const rawSize = typeof (message as any)?.rawSize === 'number' ? (message as any).rawSize : undefined;\n\n return { from, to, headers, raw, rawSize };\n}\n\nexport interface CloudflareEmailHandlerOptions {\n /**\n * Optional recipient allowlist for emails processed by this handler (case-insensitive).\n * If unset, any `to` is accepted.\n */\n expectedRecipient?: string;\n /**\n * If true (default), unexpected recipients only log a warning (Cloudflare routing\n * is usually already scoped). If false, the handler rejects the email.\n */\n allowUnexpectedRecipient?: boolean;\n /**\n * When true, logs email metadata (from/to/subject). Defaults to false.\n */\n verbose?: boolean;\n // Optional logger; defaults to silent.\n logger?: RouterLogger | null;\n}\n\nexport function createCloudflareEmailHandler(service: AuthService, opts: CloudflareEmailHandlerOptions = {}): EmailHandler {\n const logger = normalizeRouterLogger(opts.logger);\n const expectedRecipient = normalizeEmailAddress(opts.expectedRecipient || '');\n const allowUnexpectedRecipient = opts.allowUnexpectedRecipient !== false;\n const verbose = Boolean(opts.verbose);\n\n return async (message: CfEmailMessage): Promise<void> => {\n try {\n const payload = await buildForwardableEmailPayloadFromCloudflareMessage(message);\n\n const to = normalizeEmailAddress(payload.to);\n if (expectedRecipient) {\n if (to !== expectedRecipient) {\n logger.warn('[email] unexpected recipient', { to, expectedRecipient });\n if (!allowUnexpectedRecipient) {\n message.setReject('Email recovery relayer rejected email: unexpected recipient');\n return;\n }\n }\n }\n\n if (verbose) {\n logger.info('[email] from/to', { from: payload.from, to: payload.to, subject: payload.headers['subject'] });\n }\n\n const parsed = parseRecoverEmailRequest(payload as any, { headers: payload.headers });\n if (!parsed.ok) {\n logger.warn('[email] rejecting', { code: parsed.code, message: parsed.message });\n message.setReject(`Email recovery relayer rejected email: ${parsed.message}`);\n return;\n }\n\n if (!service.emailRecovery) {\n logger.warn('[email] rejecting: EmailRecoveryService not configured');\n message.setReject('Recovery relayer rejected email: email recovery service unavailable');\n return;\n }\n\n const result = await service.emailRecovery.requestEmailRecovery({\n accountId: parsed.accountId,\n emailBlob: parsed.emailBlob,\n explicitMode: parsed.explicitMode,\n });\n\n if (!result?.success) {\n logger.warn('[email] recovery failed', { accountId: parsed.accountId, error: result?.error || 'unknown' });\n message.setReject(`Email recovery relayer rejected email: ${result?.error || 'recovery failed'}`);\n return;\n }\n\n logger.info('[email] recovery submitted', { accountId: parsed.accountId });\n } catch (e: any) {\n logger.error('[email] internal error', { message: e?.message || String(e) });\n message.setReject('Email recovery relayer rejected email: internal error');\n }\n };\n}\n\nexport function createCloudflareRouter(service: AuthService, opts: RelayRouterOptions = {}): FetchHandler {\n const notFound = () => new Response('Not Found', { status: 404 });\n const mePath = opts.sessionRoutes?.auth || '/session/auth';\n const logoutPath = opts.sessionRoutes?.logout || '/session/logout';\n const logger = normalizeRouterLogger(opts.logger);\n const signedDelegatePath = (() => {\n if (!opts.signedDelegate) return '';\n const raw = String(opts.signedDelegate.route || '').trim();\n if (!raw) throw new Error('RelayRouterOptions.signedDelegate.route is required');\n return normalizePath(raw);\n })();\n const signedDelegatePolicy = opts.signedDelegate?.policy;\n\n return async function handler(request: Request, env?: CfEnv, ctx?: CfExecutionContext): Promise<Response> {\n const url = new URL(request.url);\n const { pathname } = url;\n const method = request.method.toUpperCase();\n\n // Preflight CORS\n if (method === 'OPTIONS') {\n const res = new Response(null, { status: 204 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n // Helper to adapt AuthService HTTP-like handlers to Response\n const toResponse = (out: { status: number; headers: Record<string, string>; body: string }) => {\n const res = new Response(out.body, { status: out.status, headers: out.headers });\n withCors(res.headers, opts, request);\n return res;\n };\n\n const isObject = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null;\n\n try {\n // ROR well-known manifest; allow override via env (optional)\n if (method === 'GET' && (pathname === '/.well-known/webauthn' || pathname === '/.well-known/webauthn/')) {\n const contractId = (env?.ROR_CONTRACT_ID || env?.WEBAUTHN_CONTRACT_ID || '').toString().trim() || undefined;\n const methodName = (env?.ROR_METHOD || '').toString().trim() || undefined;\n const origins = await service.getRorOrigins({ contractId, method: methodName });\n const res = json({ origins }, { status: 200, headers: { 'Cache-Control': 'max-age=60, stale-while-revalidate=600' } });\n withCors(res.headers, opts, request);\n return res;\n }\n\n if (method === 'POST' && pathname === '/create_account_and_register_user') {\n let body: unknown;\n try { body = await request.json(); } catch { body = null; }\n if (!isObject(body)) {\n const res = json({ code: 'invalid_body', message: 'JSON body required' }, { status: 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n const new_account_id = typeof body.new_account_id === 'string' ? body.new_account_id : '';\n const new_public_key = typeof body.new_public_key === 'string' ? body.new_public_key : '';\n const vrf_data = isObject(body.vrf_data) ? body.vrf_data : null;\n const webauthn_registration = isObject(body.webauthn_registration) ? body.webauthn_registration : null;\n const deterministic_vrf_public_key = (body as Record<string, unknown>).deterministic_vrf_public_key;\n const authenticator_options = isObject((body as Record<string, unknown>).authenticator_options)\n ? (body as Record<string, unknown>).authenticator_options\n : undefined;\n\n if (!new_account_id) {\n const res = json({ code: 'invalid_body', message: 'Missing or invalid new_account_id' }, { status: 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n if (!new_public_key) {\n const res = json({ code: 'invalid_body', message: 'Missing or invalid new_public_key' }, { status: 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n if (!vrf_data) {\n const res = json({ code: 'invalid_body', message: 'Missing or invalid vrf_data' }, { status: 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n if (!webauthn_registration) {\n const res = json({ code: 'invalid_body', message: 'Missing or invalid webauthn_registration' }, { status: 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n const input = {\n new_account_id,\n new_public_key,\n vrf_data,\n webauthn_registration,\n deterministic_vrf_public_key,\n authenticator_options,\n } as unknown as CreateAccountAndRegisterRequest;\n\n const result = await service.createAccountAndRegisterUser(input);\n const res = json(result, { status: result.success ? 200 : 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n // SignedDelegate meta-tx submission (optional)\n if (signedDelegatePath && pathname === signedDelegatePath) {\n if (method === 'OPTIONS') {\n const res = new Response(null, { status: 204 });\n withCors(res.headers, opts, request);\n return res;\n }\n if (method !== 'POST') return notFound();\n\n let body: unknown;\n try { body = await request.json(); } catch { body = null; }\n const valid = isObject(body) && typeof (body as any).hash === 'string' && Boolean((body as any).hash) && Boolean((body as any).signedDelegate);\n if (!valid) {\n const res = json({ ok: false, code: 'invalid_body', message: 'Expected { hash, signedDelegate }' }, { status: 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n const result = await service.executeSignedDelegate({\n hash: String((body as any).hash),\n signedDelegate: (body as any).signedDelegate,\n policy: signedDelegatePolicy,\n });\n\n if (!result || !result.ok) {\n const res = json({\n ok: false,\n code: result?.code || 'delegate_execution_failed',\n message: result?.error || 'Failed to execute delegate action',\n }, { status: 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n const res = json({\n ok: true,\n relayerTxHash: result.transactionHash || null,\n status: 'submitted',\n outcome: result.outcome ?? null,\n }, { status: 200 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n if (method === 'POST' && pathname === '/vrf/apply-server-lock') {\n const shamir = service.shamirService;\n if (!shamir || !(await shamir.ensureReady())) {\n const res = json({ code: 'shamir_disabled', message: 'Shamir 3-pass is not configured on this server' }, { status: 503 });\n withCors(res.headers, opts, request);\n return res;\n }\n let body: unknown; try { body = await request.json(); } catch { body = null; }\n const valid = isObject(body) && typeof body.kek_c_b64u === 'string' && body.kek_c_b64u.length > 0;\n if (!valid) {\n const res = json({ code: 'invalid_body', message: 'kek_c_b64u is required' }, { status: 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n const out = await handleApplyServerLock(shamir, {\n body: { kek_c_b64u: String((body as Record<string, unknown>).kek_c_b64u) },\n });\n return toResponse(out);\n }\n\n if (method === 'POST' && pathname === '/verify-authentication-response') {\n let body: unknown; try { body = await request.json(); } catch { body = null; }\n const valid = isObject(body)\n && isObject((body as any).vrf_data)\n && isObject((body as any).webauthn_authentication);\n if (!valid) {\n const res = json({ code: 'invalid_body', message: 'vrf_data and webauthn_authentication are required' }, { status: 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n try {\n const sessionKind = ((body as any)?.sessionKind || (body as any)?.session_kind) === 'cookie' ? 'cookie' : 'jwt';\n const result = await service.verifyAuthenticationResponse(body as any);\n const status = result.success ? 200 : 400;\n if (status !== 200) {\n const res = json({ code: 'not_verified', message: result.message || 'Authentication verification failed' }, { status });\n withCors(res.headers, opts, request);\n return res;\n }\n const res = json(result, { status: 200 });\n const session = opts.session;\n if (session && result.verified) {\n try {\n const userId = String((body as any).vrf_data?.user_id || '');\n const token = await session.signJwt(userId, { rpId: (body as any).vrf_data?.rp_id, blockHeight: (body as any).vrf_data?.block_height });\n logger.info(`[relay] creating ${sessionKind === 'cookie' ? 'HttpOnly session' : 'JWT'} for`, userId);\n if (sessionKind === 'cookie') {\n res.headers.set('Set-Cookie', session.buildSetCookie(token));\n } else {\n const payload = await res.clone().json();\n return new Response(JSON.stringify({ ...payload, jwt: token }), { status: 200, headers: res.headers });\n }\n } catch {}\n }\n withCors(res.headers, opts, request);\n return res;\n } catch (e: any) {\n const res = json({ code: 'internal', message: e?.message || 'Internal error' }, { status: 500 });\n withCors(res.headers, opts, request);\n return res;\n }\n }\n\n if (method === 'GET' && pathname === mePath) {\n try {\n const headersObj: Record<string, string> = {};\n request.headers.forEach((v, k) => { headersObj[k] = v; });\n const session = opts.session;\n if (!session) {\n const res = json({ authenticated: false, code: 'sessions_disabled', message: 'Sessions are not configured' }, { status: 501 });\n withCors(res.headers, opts, request);\n return res;\n }\n const parsed = await session.parse(headersObj);\n const res = json(parsed.ok ? { authenticated: true, claims: (parsed as any).claims } : { authenticated: false }, { status: parsed.ok ? 200 : 401 });\n withCors(res.headers, opts, request);\n return res;\n } catch (e: any) {\n const res = json({ authenticated: false, code: 'internal', message: e?.message || 'Internal error' }, { status: 500 });\n withCors(res.headers, opts, request);\n return res;\n }\n }\n\n if (method === 'POST' && pathname === logoutPath) {\n const res = json({ success: true }, { status: 200 });\n const session = opts.session;\n if (session) {\n // Clear cookie with Max-Age=0\n res.headers.set('Set-Cookie', session.buildClearCookie());\n }\n withCors(res.headers, opts, request);\n return res;\n }\n\n if (method === 'POST' && pathname === '/session/refresh') {\n let body: unknown; try { body = await request.json(); } catch { body = null; }\n const sessionKind = ((body as any)?.sessionKind || (body as any)?.session_kind) === 'cookie' ? 'cookie' : 'jwt';\n const session = opts.session;\n if (!session) {\n const res = json({ code: 'sessions_disabled', message: 'Sessions are not configured' }, { status: 501 });\n withCors(res.headers, opts, request);\n return res;\n }\n const out = await session.refresh(Object.fromEntries(request.headers.entries()));\n if (!out.ok || !out.jwt) {\n const res = json({ code: out.code || 'not_eligible', message: out.message || 'Refresh not eligible' }, { status: (out.code === 'unauthorized') ? 401 : 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n const res = json(sessionKind === 'cookie' ? { ok: true } : { ok: true, jwt: out.jwt }, { status: 200 });\n if (sessionKind === 'cookie' && out.jwt) {\n try {\n res.headers.set('Set-Cookie', session.buildSetCookie(out.jwt));\n } catch {}\n }\n withCors(res.headers, opts, request);\n return res;\n }\n\n if (method === 'POST' && pathname === '/recover-email') {\n const prefer = String(request.headers.get('prefer') || '').toLowerCase();\n const respondAsync =\n prefer.includes('respond-async') ||\n String(url.searchParams.get('async') || '').trim() === '1' ||\n String(url.searchParams.get('respond_async') || '').trim() === '1';\n\n let rawBody: unknown; try { rawBody = await request.json(); } catch { rawBody = null; }\n const parsed = parseRecoverEmailRequest(rawBody, { headers: request.headers });\n if (!parsed.ok) {\n const res = json({ code: parsed.code, message: parsed.message }, { status: parsed.status });\n withCors(res.headers, opts, request);\n return res;\n }\n const { accountId, emailBlob, explicitMode } = parsed;\n\n if (!service.emailRecovery) {\n const res = json(\n { code: 'email_recovery_unavailable', message: 'EmailRecoveryService is not configured on this server' },\n { status: 503 }\n );\n withCors(res.headers, opts, request);\n return res;\n }\n\n if (respondAsync && ctx && typeof ctx.waitUntil === 'function') {\n ctx.waitUntil(\n service.emailRecovery\n .requestEmailRecovery({ accountId, emailBlob, explicitMode })\n .then((result) => {\n logger.info('[recover-email] async complete', {\n success: result?.success === true,\n accountId,\n error: result?.success ? undefined : result?.error,\n });\n })\n .catch((err: any) => {\n logger.error('[recover-email] async error', {\n accountId,\n error: err?.message || String(err),\n });\n })\n );\n const res = json({ success: true, queued: true, accountId }, { status: 202 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n const result = await service.emailRecovery.requestEmailRecovery({ accountId, emailBlob, explicitMode });\n const res = json(result, { status: result.success ? 202 : 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n if (method === 'POST' && pathname === '/vrf/remove-server-lock') {\n const shamir = service.shamirService;\n if (!shamir || !(await shamir.ensureReady())) {\n const res = json({ code: 'shamir_disabled', message: 'Shamir 3-pass is not configured on this server' }, { status: 503 });\n withCors(res.headers, opts, request);\n return res;\n }\n let body: unknown; try { body = await request.json(); } catch { body = null; }\n const valid = isObject(body)\n && typeof body.kek_cs_b64u === 'string' && body.kek_cs_b64u.length > 0\n && typeof (body as Record<string, unknown>).keyId === 'string'\n && String((body as Record<string, unknown>).keyId).length > 0;\n if (!valid) {\n const res = json({ code: 'invalid_body', message: 'kek_cs_b64u and keyId are required' }, { status: 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n const out = await handleRemoveServerLock(shamir, {\n body: {\n kek_cs_b64u: String((body as Record<string, unknown>).kek_cs_b64u),\n keyId: String((body as Record<string, unknown>).keyId),\n },\n });\n return toResponse(out);\n }\n\n if (method === 'GET' && pathname === '/shamir/key-info') {\n const shamir = service.shamirService;\n if (!shamir || !(await shamir.ensureReady())) {\n const res = json({ code: 'shamir_disabled', message: 'Shamir 3-pass is not configured on this server' }, { status: 503 });\n withCors(res.headers, opts, request);\n return res;\n }\n const out = await handleGetShamirKeyInfo(shamir);\n return toResponse(out);\n }\n\n if (opts.healthz && method === 'GET' && pathname === '/healthz') {\n // Surface simple CORS info for diagnostics (normalized)\n const allowed = buildCorsOrigins(...(opts.corsOrigins || []));\n const corsAllowed = allowed === '*' ? '*' : allowed;\n const shamir = service.shamirService;\n const shamirConfigured = Boolean(shamir && shamir.hasShamir());\n let currentKeyId: string | null = null;\n if (shamirConfigured && shamir) {\n try {\n const { currentKeyId: id } = JSON.parse((await handleGetShamirKeyInfo(shamir)).body) as { currentKeyId?: string };\n currentKeyId = id || null;\n } catch {}\n }\n\n const proverBaseUrl = service.emailRecovery?.getZkEmailProverBaseUrl?.() ?? null;\n const zkEmailConfigured = Boolean(proverBaseUrl);\n\n const res = json({\n ok: true,\n // Backwards-compatible field (was previously top-level).\n currentKeyId,\n shamir: { configured: shamirConfigured, currentKeyId },\n zkEmail: { configured: zkEmailConfigured, proverBaseUrl },\n cors: { allowedOrigins: corsAllowed },\n }, { status: 200 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n if (opts.readyz && method === 'GET' && pathname === '/readyz') {\n const allowed = buildCorsOrigins(...(opts.corsOrigins || []));\n const corsAllowed = allowed === '*' ? '*' : allowed;\n\n const shamir = service.shamirService;\n const shamirConfigured = Boolean(shamir && shamir.hasShamir());\n\n let shamirReady: boolean | null = null;\n let shamirCurrentKeyId: string | null = null;\n let shamirError: string | undefined;\n if (shamirConfigured && shamir) {\n try {\n await shamir.ensureReady();\n shamirReady = true;\n const { currentKeyId } = JSON.parse((await handleGetShamirKeyInfo(shamir)).body) as { currentKeyId?: string };\n shamirCurrentKeyId = currentKeyId || null;\n } catch (e: any) {\n shamirReady = false;\n shamirError = e?.message || String(e);\n }\n }\n\n const zk = service.emailRecovery\n ? await service.emailRecovery.checkZkEmailProverHealth()\n : { configured: false, baseUrl: null, healthy: null as boolean | null };\n\n const ok =\n (shamirConfigured ? shamirReady === true : true) &&\n (zk.configured ? zk.healthy === true : true);\n\n const res = json({\n ok,\n shamir: {\n configured: shamirConfigured,\n ready: shamirConfigured ? shamirReady : null,\n currentKeyId: shamirCurrentKeyId,\n error: shamirError,\n },\n zkEmail: zk,\n cors: { allowedOrigins: corsAllowed },\n }, { status: ok ? 200 : 503 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n return notFound();\n } catch (e: unknown) {\n const res = json({ code: 'internal', message: e instanceof Error ? e.message : String(e) }, { status: 500 });\n withCors(res.headers, opts, request);\n return res;\n }\n };\n}\n\n/**\n * Optional cron hook factory for Cloudflare Workers.\n * Default is inactive (no-op). Enable explicitly in your Worker entry.\n *\n * Example:\n * const cron = createCloudflareCron(service, { enabled: env.ENABLE_ROTATION === '1', rotate: false });\n * export default { fetch: router, scheduled: cron };\n */\nexport interface CloudflareCronOptions {\n enabled?: boolean; // default false\n rotate?: boolean; // if true, will attempt to rotate Shamir keypair (not persisted in Workers)\n // Optional logger; defaults to silent.\n logger?: RouterLogger | null;\n}\n\nexport function createCloudflareCron(service: AuthService, opts: CloudflareCronOptions = {}): ScheduledHandler {\n const logger = normalizeRouterLogger(opts.logger);\n const enabled = Boolean(opts.enabled);\n const doRotate = Boolean(opts.rotate);\n if (!enabled) {\n return async () => { /* no-op by default */ };\n }\n return async (_event: CfScheduledEvent) => {\n try {\n if (doRotate) {\n // Rotation in Workers is ephemeral unless you persist keys externally.\n // This call rotates in-memory only and logs the result.\n const shamir = service.shamirService;\n if (!shamir) {\n logger.warn('[cloudflare-cron] Shamir not configured; skipping rotation');\n } else {\n const rotation = await shamir.rotateShamirServerKeypair();\n logger.info('[cloudflare-cron] rotated key', rotation.newKeyId, 'graceIds:', rotation.graceKeyIds);\n }\n } else {\n logger.info('[cloudflare-cron] enabled but rotate=false (no action)');\n }\n } catch (e: unknown) {\n logger.error('[cloudflare-cron] failed', e instanceof Error ? e.message : String(e));\n }\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cloudflare.js","names":["out: ShamirApplyServerLockResponse","e: any","out: ShamirRemoveServerLockResponse","normalizedHeaders: Record<string, string>","v","base: Logger","allowedOrigin: string | '*' | undefined","out: Record<string, string>","e: any","body: unknown","res","headersObj: Record<string, string>","rawBody: unknown","result","currentKeyId: string | null","shamirReady: boolean | null","shamirCurrentKeyId: string | null","shamirError: string | undefined","e: unknown"],"sources":["../../../../src/core/WalletIframe/validation.ts","../../../../src/server/core/shamirHandlers.ts","../../../../src/server/core/SessionService.ts","../../../../src/server/email-recovery/zkEmail/index.ts","../../../../src/server/email-recovery/emailParsers.ts","../../../../src/server/core/logger.ts","../../../../src/server/router/logger.ts","../../../../src/server/router/cloudflare-adaptor.ts"],"sourcesContent":["// Shared runtime validation helpers for Wallet Iframe code.\n\nexport function isObject(x: unknown): x is Record<string, unknown> {\n return x !== null && typeof x === 'object';\n}\n\nexport function isString(x: unknown): x is string {\n return typeof x === 'string';\n}\n\nexport function isNonEmptyString(x: unknown): x is string {\n return typeof x === 'string' && x.length > 0;\n}\n\nexport function isNumber(x: unknown): x is number {\n return typeof x === 'number';\n}\n\nexport function isFiniteNumber(x: unknown): x is number {\n return typeof x === 'number' && Number.isFinite(x);\n}\n\nexport function isFunction(x: unknown): x is Function {\n return typeof x === 'function';\n}\n\nexport function isBoolean(x: unknown): x is boolean {\n return typeof x === 'boolean';\n}\n\nexport function isArray<T = unknown>(x: unknown): x is T[] {\n return Array.isArray(x);\n}\n\n// ========= Assertions (throw on mismatch) =========\n\nexport function assertString(val: unknown, name = 'value'): string {\n if (typeof val !== 'string') throw new Error(`Invalid ${name}: expected string`);\n return val;\n}\n\nexport function assertNumber(val: unknown, name = 'value'): number {\n if (typeof val !== 'number' || !Number.isFinite(val)) throw new Error(`Invalid ${name}: expected finite number`);\n return val;\n}\n\nexport function assertBoolean(val: unknown, name = 'value'): boolean {\n if (typeof val !== 'boolean') throw new Error(`Invalid ${name}: expected boolean`);\n return val;\n}\n\nexport function assertObject<T extends Record<string, unknown> = Record<string, unknown>>(val: unknown, name = 'value'): T {\n if (!isObject(val)) throw new Error(`Invalid ${name}: expected object`);\n return val as T;\n}\n\nexport function assertArray<T = unknown>(val: unknown, name = 'value'): T[] {\n if (!Array.isArray(val)) throw new Error(`Invalid ${name}: expected array`);\n return val as T[];\n}\n\n// Shallowly remove function-valued properties (postMessage/clone safety)\nexport function stripFunctionsShallow<T extends Record<string, unknown>>(obj?: T): Partial<T> | undefined {\n if (!obj || !isObject(obj)) return undefined;\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (!isFunction(v)) out[k] = v as unknown;\n }\n return out as Partial<T>;\n}\n\n// ===============================\n// SignedTransaction shape helpers\n// ===============================\n\nexport interface PlainSignedTransactionLike {\n transaction: unknown;\n signature: unknown;\n borsh_bytes?: unknown;\n borshBytes?: unknown;\n base64Encode?: unknown;\n}\n\nexport function isPlainSignedTransactionLike(x: unknown): x is PlainSignedTransactionLike {\n if (!isObject(x)) return false;\n const hasTx = 'transaction' in x;\n const hasSig = 'signature' in x;\n const bytes = x as { borsh_bytes?: unknown; borshBytes?: unknown };\n const hasBytes = Array.isArray(bytes.borsh_bytes) || bytes.borshBytes instanceof Uint8Array;\n const hasMethod = typeof (x as { base64Encode?: unknown }).base64Encode === 'function';\n return hasTx && hasSig && hasBytes && !hasMethod;\n}\n\nexport function extractBorshBytesFromPlainSignedTx(x: PlainSignedTransactionLike): number[] {\n const asArray = Array.isArray(x.borsh_bytes) ? (x.borsh_bytes as number[]) : undefined;\n if (asArray) return asArray;\n const asU8 = (x.borshBytes instanceof Uint8Array) ? x.borshBytes : undefined;\n return Array.from(asU8 || new Uint8Array());\n}\n","import type {\n ShamirApplyServerLockRequest,\n ShamirApplyServerLockResponse,\n ShamirRemoveServerLockRequest,\n ShamirRemoveServerLockResponse,\n} from './types';\nimport type { ShamirService } from './ShamirService';\nimport { isString } from '../../core/WalletIframe/validation';\n\nexport async function handleApplyServerLock(\n service: ShamirService,\n request: { body?: { kek_c_b64u?: string } },\n): Promise<{ status: number; headers: Record<string, string>; body: string }> {\n try {\n const kek_c_b64u = request.body?.kek_c_b64u;\n if (!isString(kek_c_b64u) || !kek_c_b64u) {\n return {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'kek_c_b64u required and must be a non-empty string' }),\n };\n }\n\n const out: ShamirApplyServerLockResponse = await service.applyServerLock(kek_c_b64u);\n const keyId = service.getCurrentShamirKeyId();\n\n return {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ ...out, keyId }),\n };\n } catch (e: any) {\n return {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'internal', details: e?.message }),\n };\n }\n}\n\nexport async function handleRemoveServerLock(\n service: ShamirService,\n request: { body?: { kek_cs_b64u?: string; keyId?: string } },\n): Promise<{ status: number; headers: Record<string, string>; body: string }> {\n try {\n const body = request.body;\n if (!body) {\n return {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'Missing body' }),\n };\n }\n const { kek_cs_b64u, keyId } = body;\n if (!isString(kek_cs_b64u) || !kek_cs_b64u) {\n return {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'kek_cs_b64u required and must be a non-empty string' }),\n };\n }\n if (!isString(keyId) || !keyId) {\n return {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'keyId required and must be a non-empty string' }),\n };\n }\n\n const providedKeyId = String(keyId);\n const currentKeyId = service.getCurrentShamirKeyId();\n let out: ShamirRemoveServerLockResponse;\n\n if (currentKeyId && providedKeyId === currentKeyId) {\n out = await service.removeServerLock(kek_cs_b64u);\n } else if (service.hasGraceKey(providedKeyId)) {\n out = await service.removeGraceServerLockWithKey(providedKeyId, { kek_cs_b64u } as ShamirRemoveServerLockRequest);\n } else {\n return {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'unknown keyId' }),\n };\n }\n\n return {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(out),\n };\n } catch (e: any) {\n return {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'internal', details: e?.message }),\n };\n }\n}\n\nexport async function handleGetShamirKeyInfo(\n service: ShamirService,\n): Promise<{ status: number; headers: Record<string, string>; body: string }> {\n try {\n await service.ensureReady();\n const currentKeyId = service.getCurrentShamirKeyId();\n const graceKeyIds = service.getGraceKeyIds();\n\n return {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n currentKeyId,\n p_b64u: service.getShamirConfig()?.shamir_p_b64u ?? null,\n graceKeyIds,\n }),\n };\n } catch (e: any) {\n return {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'internal', details: e?.message }),\n };\n }\n}\n\nexport async function handleListGraceKeys(\n service: ShamirService,\n): Promise<{ status: number; headers: Record<string, string>; body: string }> {\n try {\n await service.ensureGraceKeysLoaded();\n return {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ graceKeyIds: service.getGraceKeyIds() }),\n };\n } catch (e: any) {\n return {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'internal', details: e?.message }),\n };\n }\n}\n\nexport async function handleAddGraceKey(\n service: ShamirService,\n request: { e_s_b64u?: string; d_s_b64u?: string },\n): Promise<{ status: number; headers: Record<string, string>; body: string }> {\n try {\n const { e_s_b64u, d_s_b64u } = request || ({} as any);\n if (!isString(e_s_b64u) || !e_s_b64u || !isString(d_s_b64u) || !d_s_b64u) {\n return {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'e_s_b64u and d_s_b64u required' }),\n };\n }\n\n await service.ensureGraceKeysLoaded();\n const added = await service.addGraceKeyInternal({ e_s_b64u, d_s_b64u }, { persist: true, skipIfExists: true });\n if (!added) {\n return {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'failed to add grace key' }),\n };\n }\n\n return {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ keyId: added.keyId }),\n };\n } catch (e: any) {\n return {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'internal', details: e?.message }),\n };\n }\n}\n\nexport async function handleRemoveGraceKey(\n service: ShamirService,\n request: { keyId?: string },\n): Promise<{ status: number; headers: Record<string, string>; body: string }> {\n try {\n const keyId = request?.keyId;\n if (!isString(keyId) || !keyId) {\n return {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'keyId required and must be a non-empty string' }),\n };\n }\n\n const removed = await service.removeGraceKeyInternal(keyId, { persist: true });\n return {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ removed }),\n };\n } catch (e: any) {\n return {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ error: 'internal', details: e?.message }),\n };\n }\n}\n","\nexport interface SessionConfig {\n jwt?: {\n /** Required: JWT signing hook; return a complete token */\n signToken?: (input: { header: Record<string, unknown>; payload: Record<string, unknown> }) => Promise<string> | string;\n /** Required: JWT verification hook */\n verifyToken?: (token: string) => Promise<{ valid: boolean; payload?: any }> | { valid: boolean; payload?: any };\n /** Optional: sliding refresh window (seconds) to allow /session/refresh before exp, default 900 (15 min) */\n refreshWindowSec?: number;\n /** Optional: build additional claims to include in the payload */\n buildClaims?: (input: { sub: string; context?: Record<string, unknown> }) => Promise<Record<string, unknown> | void> | Record<string, unknown> | void;\n };\n cookie?: {\n /** Cookie name. Default: 'w3a_session' */\n name?: string;\n /** Optional override: build Set-Cookie header for a new token */\n buildSetHeader?: (token: string) => string;\n /** Optional override: build Set-Cookie header that clears the cookie */\n buildClearHeader?: () => string;\n /** Optional override: extract token from headers (Authorization/Cookie) */\n extractToken?: (headers: Record<string, string | string[] | undefined>, cookieName: string) => string | null;\n };\n}\n\nexport class SessionService<TClaims = any> {\n private cfg: NonNullable<SessionConfig>;\n\n constructor(cfg: NonNullable<SessionConfig>) {\n this.cfg = cfg || ({} as any);\n }\n\n getCookieName(): string {\n return this.cfg?.cookie?.name || 'w3a_session';\n }\n\n buildSetCookie(token: string): string {\n if (this.cfg?.cookie?.buildSetHeader) return this.cfg.cookie.buildSetHeader(token);\n const name = this.getCookieName();\n const cookieParts = [`${name}=${token}`];\n const path = '/';\n const httpOnly = true;\n const secure = true; // default secure\n const sameSite = 'Lax';\n const maxAge = 24 * 3600; // 1 day default\n cookieParts.push(`Path=${path}`);\n if (httpOnly) cookieParts.push('HttpOnly');\n if (secure) cookieParts.push('Secure');\n if (sameSite) cookieParts.push(`SameSite=${sameSite}`);\n if (maxAge) {\n cookieParts.push(`Max-Age=${maxAge}`);\n const expires = new Date(Date.now() + (maxAge * 1000)).toUTCString();\n cookieParts.push(`Expires=${expires}`);\n }\n return cookieParts.join('; ');\n }\n\n buildClearCookie(): string {\n if (this.cfg?.cookie?.buildClearHeader) return this.cfg.cookie.buildClearHeader();\n const name = this.getCookieName();\n const path = '/';\n const secure = true;\n const httpOnly = true;\n const parts = [\n `${name}=`,\n `Path=${path}`,\n 'Max-Age=0',\n 'Expires=Thu, 01 Jan 1970 00:00:00 GMT'\n ];\n if (httpOnly) parts.push('HttpOnly');\n if (secure) parts.push('Secure');\n const sameSite = 'Lax';\n if (sameSite) parts.push(`SameSite=${sameSite}`);\n return parts.join('; ');\n }\n\n /** Sign a JWT with configured algorithm. Adds iat/exp and copies iss/aud. */\n async signJwt(sub: string, extraClaims?: Record<string, unknown>): Promise<string> {\n const jwt = this.cfg?.jwt || {};\n const built = await Promise.resolve(jwt.buildClaims?.({ sub, context: extraClaims })) || {};\n const payload = { sub, ...(extraClaims || {}), ...(built || {}) } as Record<string, unknown>;\n if (typeof jwt.signToken === 'function') {\n // Full override of signing: user supplies the complete token\n const token = await Promise.resolve(jwt.signToken({ header: { typ: 'JWT' }, payload } as any));\n return token;\n }\n throw new Error('SessionService: No JWT signing hook or provider configured');\n }\n\n /** Verify signature and expiration. Returns payload on success. */\n async verifyJwt(token: string): Promise<{ valid: boolean; payload?: any }> {\n const verify = this.cfg?.jwt?.verifyToken;\n if (typeof verify !== 'function') return { valid: false };\n return await Promise.resolve(verify(token));\n }\n\n parse(headers: Record<string, string | string[] | undefined>): Promise<{ ok: boolean; claims?: TClaims } | { ok: false }>{\n const authHeader = (headers['authorization'] || headers['Authorization']) as string | undefined;\n let token: string | null = null;\n if (authHeader && /^Bearer\\s+/.test(authHeader)) token = authHeader.replace(/^Bearer\\s+/i, '').trim();\n const cookieHeader = (headers['cookie'] || headers['Cookie']) as string | undefined;\n if (!token && cookieHeader) {\n const name = this.getCookieName();\n for (const part of cookieHeader.split(';')) {\n const [k, v] = part.split('=');\n if (k && k.trim() === name) { token = (v || '').trim(); break; }\n }\n }\n if (!token) return Promise.resolve({ ok: false });\n return this.verifyJwt(token).then(v => v.valid ? { ok: true, claims: v.payload as TClaims } : { ok: false });\n }\n\n // === token helpers ===\n extractTokenFromHeaders(headers: Record<string, string | string[] | undefined>): string | null {\n if (this.cfg?.cookie?.extractToken) return this.cfg.cookie.extractToken(headers, this.getCookieName());\n const authHeader = (headers['authorization'] || headers['Authorization']) as string | undefined;\n if (authHeader && /^Bearer\\s+/.test(authHeader)) return authHeader.replace(/^Bearer\\s+/i, '').trim();\n const cookieHeader = (headers['cookie'] || headers['Cookie']) as string | undefined;\n if (cookieHeader) {\n const name = this.getCookieName();\n for (const part of cookieHeader.split(';')) {\n const [k, v] = part.split('=');\n if (k && k.trim() === name) return (v || '').trim();\n }\n }\n return null;\n }\n\n async refresh(headers: Record<string, string | string[] | undefined>): Promise<{ ok: boolean; jwt?: string; code?: string; message?: string }>{\n try {\n const token = this.extractTokenFromHeaders(headers);\n if (!token) return { ok: false, code: 'unauthorized', message: 'No session token' };\n const v = await this.verifyJwt(token);\n if (!v.valid) return { ok: false, code: 'unauthorized', message: 'Invalid token' };\n const payload: any = v.payload || {};\n if (!this.isWithinRefreshWindow(payload)) return { ok: false, code: 'not_eligible', message: 'Not within refresh window' };\n const sub = String(payload.sub || '');\n if (!sub) return { ok: false, code: 'invalid_claims', message: 'Missing sub claim' };\n const next = await this.signJwt(sub);\n return { ok: true, jwt: next };\n } catch (e: any) {\n return { ok: false, code: 'internal', message: e?.message || 'Refresh failed' };\n }\n }\n\n nowSeconds(): number { return Math.floor(Date.now() / 1000); }\n\n private isWithinRefreshWindow(payload: any): boolean {\n try {\n const now = this.nowSeconds();\n const exp = Number(payload?.exp || 0);\n if (!exp || now >= exp) return false; // no refresh if already expired\n const windowSec = Number(this.cfg?.jwt?.refreshWindowSec || 15 * 60);\n return (exp - now) <= windowSec;\n } catch { return false; }\n }\n}\n\n/*\n * Utility: parse comma-separated list of origins into a normalized unique list\n * - canonicalizes to protocol + host + optional port\n * - lowercases host, strips path/query/hash, trims spaces/trailing slashes\n */\nexport function parseCsvList(input?: string): string[] {\n const out = new Set<string>();\n for (const raw of String(input || '').split(',')) {\n const s = raw.trim();\n if (!s) continue;\n try {\n const u = new URL(s);\n const host = u.hostname.toLowerCase();\n const port = u.port ? `:${u.port}` : '';\n const proto = u.protocol === 'http:' || u.protocol === 'https:' ? u.protocol : 'https:';\n out.add(`${proto}//${host}${port}`);\n } catch {\n const stripped = s.replace(/\\/$/, '');\n if (stripped) out.add(stripped);\n }\n }\n return Array.from(out);\n}\n\n/*\n * Utility: merge multiple CSV lists of origins and return normalized list or '*'\n */\nexport function buildCorsOrigins(...inputs: Array<string | undefined>): string[] | '*' {\n const merged = new Set<string>();\n for (const input of inputs) {\n for (const origin of parseCsvList(input)) merged.add(origin);\n }\n const list = Array.from(merged);\n return list.length > 0 ? list : '*';\n}\n","import { ZkEmailProverClient, type ZkEmailProverClientOptions, type ZkEmailProverError } from './proverClient';\n\nexport { ZkEmailProverClient } from './proverClient';\nexport type { ZkEmailProverClientOptions, ZkEmailProverError, ZkEmailProverResponse } from './proverClient';\n\nexport interface ForwardableEmailPayload {\n from: string;\n to: string;\n headers: Record<string, string>;\n raw?: string;\n rawSize?: number;\n}\n\nexport type NormalizedEmailResult =\n | { ok: true; payload: ForwardableEmailPayload }\n | { ok: false; code: string; message: string };\n\nexport interface GenerateZkEmailProofResult {\n proof: unknown;\n publicInputs: string[];\n}\n\nexport interface ParsedZkEmailBindings {\n accountId: string;\n newPublicKey: string;\n fromEmail: string;\n timestamp: string;\n requestId: string;\n}\n\n/**\n * Build a minimal ForwardableEmailPayload from a raw RFC822 email string.\n * This is primarily used by server-side helpers that receive only a raw\n * email blob (no pre-normalized headers).\n */\nexport function buildForwardablePayloadFromRawEmail(raw: string): ForwardableEmailPayload {\n const safeRaw = typeof raw === 'string' ? raw : '';\n const lines = safeRaw.split(/\\r?\\n/);\n\n const getHeader = (name: string): string | undefined => {\n const line = lines.find(l => new RegExp(`^${name}:`, 'i').test(l));\n if (!line) return undefined;\n const idx = line.indexOf(':');\n const rest = idx >= 0 ? line.slice(idx + 1) : '';\n const value = rest.trim();\n return value || undefined;\n };\n\n const fromHeader = getHeader('from') || 'unknown@zkemail.local';\n const toHeader = getHeader('to') || 'recover@zkemail.local';\n\n const headers: Record<string, string> = {};\n const subjectHeader = getHeader('subject');\n const dateHeader = getHeader('date');\n\n if (fromHeader) headers.from = fromHeader;\n if (toHeader) headers.to = toHeader;\n if (subjectHeader) headers.subject = subjectHeader;\n if (dateHeader) headers.date = dateHeader;\n\n return {\n from: fromHeader,\n to: toHeader,\n headers,\n raw: safeRaw,\n rawSize: safeRaw.length,\n };\n}\n\nexport function normalizeForwardableEmailPayload(input: unknown): NormalizedEmailResult {\n if (!input || typeof input !== 'object') {\n return { ok: false, code: 'invalid_email', message: 'JSON body required' };\n }\n\n const body = input as Partial<ForwardableEmailPayload>;\n const { from, to, headers, raw, rawSize } = body;\n\n if (!from || typeof from !== 'string' || !to || typeof to !== 'string') {\n return { ok: false, code: 'invalid_email', message: 'from and to are required' };\n }\n\n if (!headers || typeof headers !== 'object') {\n return { ok: false, code: 'invalid_email', message: 'headers object is required' };\n }\n\n const normalizedHeaders: Record<string, string> = {};\n for (const [k, v] of Object.entries(headers as Record<string, unknown>)) {\n normalizedHeaders[String(k).toLowerCase()] = String(v);\n }\n\n return {\n ok: true,\n payload: {\n from,\n to,\n headers: normalizedHeaders,\n raw: typeof raw === 'string' ? raw : undefined,\n rawSize: typeof rawSize === 'number' ? rawSize : undefined,\n },\n };\n}\n\n/**\n * Parse NEAR accountId from the Subject line inside a raw RFC822 email.\n *\n * Expected format (case-insensitive on \"Subject\" and \"recover\"):\n * Subject: recover-123ABC bob.testnet ed25519:<pk>\n *\n * Returns the parsed accountId (e.g. \"bob.testnet\") or null if not found.\n */\nexport function parseAccountIdFromSubject(raw: string | undefined | null): string | null {\n if (!raw || typeof raw !== 'string') return null;\n\n // Accept either a full RFC822 message (with \"Subject: ...\" header)\n // or a bare Subject value (\"recover-123ABC bob.testnet ed25519:<pk>\").\n let subjectText = '';\n const lines = raw.split(/\\r?\\n/);\n const subjectLine = lines.find(line => /^subject:/i.test(line));\n if (subjectLine) {\n const idx = subjectLine.indexOf(':');\n const restRaw = idx >= 0 ? subjectLine.slice(idx + 1) : '';\n subjectText = restRaw.trim();\n } else {\n subjectText = raw.trim();\n }\n\n if (!subjectText) return null;\n\n // Strip common reply/forward prefixes\n subjectText = subjectText.replace(/^(re|fwd):\\s*/i, '').trim();\n if (!subjectText) return null;\n\n // Strict format: \"recover-<request_id> <accountId> [ed25519:<pk>]\"\n const match = subjectText.match(\n /^recover-([A-Za-z0-9]{6})\\s+([^\\s]+)(?:\\s+ed25519:[^\\s]+)?\\s*$/i\n );\n if (match?.[2]) {\n return match[2];\n }\n\n return null;\n}\n\nfunction parseSubjectBindings(\n rawSubject: string | undefined | null\n): { accountId: string; newPublicKey: string; requestId: string } | null {\n if (!rawSubject || typeof rawSubject !== 'string') return null;\n\n const lines = rawSubject.split(/\\r?\\n/);\n let subjectText = '';\n const subjectLine = lines.find(line => /^subject:/i.test(line));\n if (subjectLine) {\n const idx = subjectLine.indexOf(':');\n const restRaw = idx >= 0 ? subjectLine.slice(idx + 1) : '';\n subjectText = restRaw.trim();\n } else {\n subjectText = rawSubject.trim();\n }\n if (!subjectText) return null;\n\n subjectText = subjectText.replace(/^(re|fwd):\\s*/i, '').trim();\n if (!subjectText) return null;\n\n // Strict format:\n // \"recover-<request_id> <accountId> ed25519:<pk>\"\n const match = subjectText.match(\n /^recover-([A-Za-z0-9]{6})\\s+([^\\s]+)\\s+ed25519:([^\\s]+)\\s*$/i\n );\n if (!match) return null;\n\n const [, requestId, accountId, newPublicKey] = match;\n\n return {\n accountId,\n newPublicKey,\n requestId,\n };\n}\n\nexport function extractZkEmailBindingsFromPayload(\n payload: ForwardableEmailPayload\n): ParsedZkEmailBindings | null {\n const raw = payload.raw || '';\n const lines = raw.split(/\\r?\\n/);\n\n const subjectLine = lines.find(line => /^subject:/i.test(line));\n const subjectBindings = parseSubjectBindings(subjectLine ?? '');\n if (!subjectBindings) {\n return null;\n }\n\n const headers = payload.headers || {};\n let fromEmailRaw: string | undefined =\n (headers['from'] as any) ||\n (headers['x-from-email'] as any);\n let dateRaw: string | undefined =\n (headers['date'] as any) ||\n (headers['x-original-date'] as any);\n\n // Fallback: if headers object does not contain from/date,\n // attempt to parse them from the raw RFC822 email lines.\n if (!fromEmailRaw || !dateRaw) {\n for (const line of lines) {\n if (!fromEmailRaw && /^from:/i.test(line)) {\n const idx = line.indexOf(':');\n fromEmailRaw = idx >= 0 ? line.slice(idx + 1).trim() : '';\n }\n if (!dateRaw && /^date:/i.test(line)) {\n const idx = line.indexOf(':');\n dateRaw = idx >= 0 ? line.slice(idx + 1).trim() : '';\n }\n if (fromEmailRaw && dateRaw) break;\n }\n }\n\n const fromEmail = String(fromEmailRaw || '').trim();\n const timestamp = String(dateRaw || '').trim();\n\n if (!fromEmail || !timestamp) {\n return null;\n }\n\n return {\n accountId: subjectBindings.accountId,\n newPublicKey: subjectBindings.newPublicKey,\n fromEmail,\n timestamp,\n requestId: subjectBindings.requestId,\n };\n}\n\nexport async function generateZkEmailProofFromPayload(\n payload: ForwardableEmailPayload,\n prover: ZkEmailProverClientOptions | ZkEmailProverClient\n): Promise<GenerateZkEmailProofResult> {\n if (!payload.raw || typeof payload.raw !== 'string') {\n const err: ZkEmailProverError = Object.assign(\n new Error('raw email contents are required to generate a zk-email proof'),\n { code: 'missing_raw_email' }\n );\n throw err;\n }\n\n const client =\n (prover && typeof (prover as any).proveEmail === 'function')\n ? (prover as ZkEmailProverClient)\n : new ZkEmailProverClient(prover as ZkEmailProverClientOptions);\n\n const res = await client.proveEmail(payload.raw);\n\n return {\n proof: res.proof,\n publicInputs: res.publicSignals,\n };\n}\n","import type { EmailRecoveryMode } from './types';\nimport { normalizeForwardableEmailPayload, parseAccountIdFromSubject } from './zkEmail';\nimport { ensureEd25519Prefix } from '../../core/nearCrypto';\n\nexport enum EmailRecoveryModeHint {\n ZkEmail = 'zk-email',\n TeeEncrypted = 'tee-encrypted',\n OnchainPublic = 'onchain-public',\n}\n\nexport function normalizeRecoveryMode(raw: string | undefined | null): EmailRecoveryMode | null {\n if (!raw) return null;\n const value = raw.trim().toLowerCase();\n if (value === EmailRecoveryModeHint.ZkEmail) return 'zk-email';\n if (value === EmailRecoveryModeHint.TeeEncrypted) return 'tee-encrypted';\n if (value === EmailRecoveryModeHint.OnchainPublic) return 'onchain-public';\n return null;\n}\n\nexport function extractRecoveryModeFromBody(emailBlob?: string): EmailRecoveryMode | null {\n if (!emailBlob) return null;\n\n const lines = emailBlob.split(/\\r?\\n/);\n const bodyStartIndex = lines.findIndex(line => line.trim() === '');\n if (bodyStartIndex === -1) return null;\n\n const bodyLines = lines.slice(bodyStartIndex + 1);\n const firstNonEmptyBodyLine = bodyLines.find(line => line.trim() !== '');\n if (!firstNonEmptyBodyLine) return null;\n\n const candidate = firstNonEmptyBodyLine.trim();\n const normalized = normalizeRecoveryMode(candidate);\n if (normalized) return normalized;\n\n const lower = candidate.toLowerCase();\n if (lower.includes(EmailRecoveryModeHint.ZkEmail)) return 'zk-email';\n if (lower.includes(EmailRecoveryModeHint.TeeEncrypted)) return 'tee-encrypted';\n if (lower.includes(EmailRecoveryModeHint.OnchainPublic)) return 'onchain-public';\n\n return null;\n}\n\ntype HeaderValue = string | string[] | undefined;\ntype HeadersLike = Headers | Record<string, HeaderValue> | undefined;\n\nexport type RecoverEmailParseResult =\n | { ok: true; accountId: string; emailBlob: string; explicitMode?: string }\n | { ok: false; status: number; code: string; message: string };\n\nfunction getHeader(headers: HeadersLike, name: string): string | undefined {\n if (!headers) return undefined;\n\n const maybeHeaders = headers as any;\n if (typeof maybeHeaders.get === 'function') {\n const v = maybeHeaders.get(name);\n return (typeof v === 'string') ? v : undefined;\n }\n\n const record = headers as Record<string, HeaderValue>;\n const v = record[name.toLowerCase()] ?? record[name];\n if (Array.isArray(v)) return (typeof v[0] === 'string') ? v[0] : undefined;\n return (typeof v === 'string') ? v : undefined;\n}\n\nfunction parseExplicitMode(body: unknown, headers?: HeadersLike): string | undefined {\n const modeFromBody =\n (typeof (body as any)?.explicitMode === 'string' ? String((body as any).explicitMode) : '') ||\n (typeof (body as any)?.explicit_mode === 'string' ? String((body as any).explicit_mode) : '');\n const modeFromHeader = getHeader(headers, 'x-email-recovery-mode') || getHeader(headers, 'x-recovery-mode') || '';\n const raw = (modeFromBody || modeFromHeader).trim();\n return raw ? raw : undefined;\n}\n\nexport function parseRecoverEmailRequest(body: unknown, opts: { headers?: HeadersLike } = {}): RecoverEmailParseResult {\n const explicitMode = parseExplicitMode(body, opts.headers);\n\n const normalized = normalizeForwardableEmailPayload(body);\n if (!normalized.ok) {\n return { ok: false, status: 400, code: normalized.code, message: normalized.message };\n }\n\n const payload = normalized.payload;\n const emailBlob = payload.raw || '';\n const emailHeaders = payload.headers || {};\n\n const subjectHeader = emailHeaders['subject'];\n const parsedAccountId = parseAccountIdFromSubject(subjectHeader || emailBlob);\n const headerAccountId = String(emailHeaders['x-near-account-id'] || emailHeaders['x-account-id'] || '').trim();\n const accountId = (parsedAccountId || headerAccountId || '').trim();\n\n if (!accountId) {\n return { ok: false, status: 400, code: 'missing_account', message: 'x-near-account-id header is required' };\n }\n if (!emailBlob) {\n return { ok: false, status: 400, code: 'missing_email', message: 'raw email blob is required' };\n }\n\n return { ok: true, accountId, emailBlob, explicitMode };\n}\n\nconst EMAIL_ADDRESS_REGEX =\n /([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*)/;\n\nexport function canonicalizeEmail(input: string): string {\n const raw = String(input || '').trim();\n if (!raw) return '';\n\n // Handle cases where a full header line is passed in (e.g. \"From: ...\").\n const withoutHeaderName = raw.replace(/^[a-z0-9-]+\\s*:\\s*/i, '').trim();\n\n // Prefer the common \"Name <email@domain>\" format when present, but still\n // validate/extract the actual address via regex.\n const angleMatch = withoutHeaderName.match(/<([^>]+)>/);\n const candidates = [\n angleMatch?.[1],\n withoutHeaderName,\n ].filter((v): v is string => typeof v === 'string' && v.length > 0);\n\n for (const candidate of candidates) {\n const cleaned = candidate.replace(/^mailto:\\s*/i, '');\n const match = cleaned.match(EMAIL_ADDRESS_REGEX);\n if (match?.[1]) {\n return match[1].trim().toLowerCase();\n }\n }\n\n return withoutHeaderName.toLowerCase();\n}\n\nexport function parseHeaderValue(rawEmail: string, name: string): string | undefined {\n try {\n const raw = String(rawEmail || '');\n if (!raw) return undefined;\n\n const lines = raw.split(/\\r?\\n/);\n const headerLines: string[] = [];\n\n // Only consider the header section (until the first blank line).\n for (const line of lines) {\n if (line.trim() === '') break;\n\n // RFC822 header folding: lines starting with whitespace continue previous header.\n if (/^\\s/.test(line) && headerLines.length > 0) {\n headerLines[headerLines.length - 1] += ` ${line.trim()}`;\n continue;\n }\n\n headerLines.push(line);\n }\n\n const headerName = name.trim();\n if (!headerName) return undefined;\n\n const re = new RegExp(`^${headerName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\s*:`, 'i');\n const found = headerLines.find((l) => re.test(l));\n if (!found) return undefined;\n\n const idx = found.indexOf(':');\n const value = idx >= 0 ? found.slice(idx + 1).trim() : '';\n return value || undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function parseRecoverSubjectBindings(\n rawEmail: string\n): { requestId: string; accountId: string; newPublicKey: string } | null {\n // Accept either a full RFC822 email or a bare Subject value.\n let subjectText = (parseHeaderValue(rawEmail, 'subject') || String(rawEmail || '')).trim();\n if (!subjectText) return null;\n\n // Strip common reply/forward prefixes.\n subjectText = subjectText.replace(/^(re|fwd):\\s*/i, '').trim();\n if (!subjectText) return null;\n\n // Strict format:\n // \"recover-<request_id> <accountId> ed25519:<pk>\"\n const match = subjectText.match(\n /^recover-([A-Za-z0-9]{6})\\s+([^\\s]+)\\s+ed25519:([^\\s]+)\\s*$/i\n );\n if (!match) return null;\n\n const [, requestId, accountId, newPublicKey] = match;\n return { requestId, accountId, newPublicKey: ensureEd25519Prefix(newPublicKey) };\n}\n","export type Logger = {\n debug?: (...args: unknown[]) => void;\n info?: (...args: unknown[]) => void;\n warn?: (...args: unknown[]) => void;\n error?: (...args: unknown[]) => void;\n log?: (...args: unknown[]) => void;\n};\n\nexport type NormalizedLogger = {\n debug: (...args: unknown[]) => void;\n info: (...args: unknown[]) => void;\n warn: (...args: unknown[]) => void;\n error: (...args: unknown[]) => void;\n};\n\nfunction safe(fn?: (...args: unknown[]) => void): (...args: unknown[]) => void {\n if (!fn) return () => {};\n return (...args: unknown[]) => {\n try {\n fn(...args);\n } catch {\n // Never allow logging to break request handling.\n }\n };\n}\n\n/**\n * Library code should never call `console.*` directly; the host decides where logs go.\n * - Default: no logs\n * - To enable: pass `logger: console` (or a structured logger) to the host config.\n */\nexport function normalizeLogger(logger?: Logger | null): NormalizedLogger {\n if (!logger) {\n return { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} };\n }\n\n const base: Logger = logger;\n const log = typeof base.log === 'function' ? base.log.bind(base) : undefined;\n\n const debug = (typeof base.debug === 'function' ? base.debug.bind(base) : log);\n const info = (typeof base.info === 'function' ? base.info.bind(base) : log);\n const warn = (typeof base.warn === 'function' ? base.warn.bind(base) : log);\n const error = (typeof base.error === 'function' ? base.error.bind(base) : log);\n\n return {\n debug: safe(debug),\n info: safe(info),\n warn: safe(warn),\n error: safe(error),\n };\n}\n\n","import type { Logger, NormalizedLogger } from '../core/logger';\nimport { normalizeLogger } from '../core/logger';\n\nexport type RouterLogger = Logger;\nexport type NormalizedRouterLogger = NormalizedLogger;\nexport const normalizeRouterLogger = normalizeLogger;\n","import type { AuthService } from '../core/AuthService';\nimport {\n handleApplyServerLock,\n handleRemoveServerLock,\n handleGetShamirKeyInfo,\n} from '../core/shamirHandlers';\nimport { buildCorsOrigins } from '../core/SessionService';\nimport type { SessionAdapter } from './express-adaptor';\nimport type { CreateAccountAndRegisterRequest } from '../core/types';\nimport { parseRecoverEmailRequest } from '../email-recovery/emailParsers';\nimport type { DelegateActionPolicy } from '../delegateAction';\nimport type { RouterLogger } from './logger';\nimport { normalizeRouterLogger } from './logger';\n\nexport interface RelayRouterOptions {\n healthz?: boolean;\n readyz?: boolean;\n // Optional list(s) of CORS origins (CSV strings or literal origins).\n // Pass raw strings; the router normalizes/merges internally.\n corsOrigins?: Array<string | undefined>;\n /**\n * Optional route for submitting NEP-461 SignedDelegate meta-transactions.\n *\n * - When omitted: disabled.\n * - When set: enabled at `route`.\n *\n * `policy` is server-controlled and is never read from the request body.\n */\n signedDelegate?: {\n route: string;\n policy?: DelegateActionPolicy;\n };\n // Optional: customize session route paths\n sessionRoutes?: { auth?: string; logout?: string };\n // Optional: pluggable session adapter\n session?: SessionAdapter | null;\n // Optional logger; defaults to silent.\n logger?: RouterLogger | null;\n}\n\n// Minimal Worker runtime types (avoid adding @cloudflare/workers-types dependency here)\nexport interface CfEnv {\n // Optional env overrides for `/.well-known/webauthn` (ROR origins list).\n //\n // Note: Do not add an index signature here. Cloudflare env bindings can include\n // KV namespaces, Durable Objects, etc., and requiring `[key: string]: string`\n // makes real-world `Env` types not assignable.\n ROR_CONTRACT_ID?: string;\n WEBAUTHN_CONTRACT_ID?: string;\n ROR_METHOD?: string;\n}\n\n/**\n * Convenience env shape matching the `examples/relay-cloudflare-worker` configuration.\n * This is optional — you can define your own `Env` type with different binding names.\n */\nexport interface RelayCloudflareWorkerEnv {\n RELAYER_ACCOUNT_ID: string;\n RELAYER_PRIVATE_KEY: string;\n // Optional overrides (SDK provides defaults when omitted)\n NEAR_RPC_URL?: string;\n NETWORK_ID?: string;\n WEBAUTHN_CONTRACT_ID: string;\n ACCOUNT_INITIAL_BALANCE?: string;\n CREATE_ACCOUNT_AND_REGISTER_GAS?: string;\n ZK_EMAIL_PROVER_BASE_URL?: string;\n ZK_EMAIL_PROVER_TIMEOUT_MS?: string;\n SHAMIR_P_B64U: string;\n SHAMIR_E_S_B64U: string;\n SHAMIR_D_S_B64U: string;\n EXPECTED_ORIGIN?: string;\n EXPECTED_WALLET_ORIGIN?: string;\n ENABLE_ROTATION?: string;\n RECOVER_EMAIL_RECIPIENT?: string;\n}\n\nexport interface CfExecutionContext {\n waitUntil(promise: Promise<unknown>): void;\n passThroughOnException(): void\n}\nexport interface CfScheduledEvent {\n scheduledTime?: number;\n cron?: string\n}\nexport interface CfEmailMessage {\n from: string;\n to: string;\n // Cloudflare uses `Headers`, but keep this flexible for userland tests.\n headers: Headers | Iterable<[string, string]> | Record<string, string>;\n raw: ReadableStream | ArrayBuffer | string;\n rawSize?: number;\n setReject(reason: string): void;\n}\n\nexport type FetchHandler = (request: Request, env?: CfEnv, ctx?: CfExecutionContext) => Promise<Response>;\nexport type ScheduledHandler = (event: CfScheduledEvent, env?: CfEnv, ctx?: CfExecutionContext) => Promise<void>;\nexport type EmailHandler = (message: CfEmailMessage, env?: CfEnv, ctx?: CfExecutionContext) => Promise<void>;\n\nfunction normalizePath(path: string): string {\n const trimmed = String(path || '').trim();\n if (!trimmed) return '';\n return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;\n}\n\nfunction json(body: unknown, init?: ResponseInit, extraHeaders?: Record<string, string>): Response {\n const headers = new Headers({ 'Content-Type': 'application/json; charset=utf-8' });\n\n // Merge init.headers into our base headers (ResponseInit headers are otherwise overwritten).\n const initHeaders = (init as any)?.headers as HeadersInit | undefined;\n if (initHeaders) {\n try {\n new Headers(initHeaders).forEach((v, k) => headers.set(k, v));\n } catch { }\n }\n\n if (extraHeaders) {\n for (const [k, v] of Object.entries(extraHeaders)) headers.set(k, v);\n }\n\n const { headers: _omit, ...rest } = init || {};\n return new Response(JSON.stringify(body), { status: 200, ...rest, headers });\n}\n\nfunction withCors(headers: Headers, opts?: RelayRouterOptions, request?: Request): void {\n if (!opts?.corsOrigins) return;\n let allowedOrigin: string | '*' | undefined;\n const normalized = buildCorsOrigins(...(opts.corsOrigins || []));\n if (normalized === '*') {\n allowedOrigin = '*';\n headers.set('Access-Control-Allow-Origin', '*');\n } else if (Array.isArray(normalized)) {\n const origin = request?.headers.get('Origin') || '';\n if (origin && normalized.includes(origin)) {\n allowedOrigin = origin;\n headers.set('Access-Control-Allow-Origin', origin);\n headers.append('Vary', 'Origin');\n }\n }\n headers.set('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');\n headers.set('Access-Control-Allow-Headers', 'Content-Type,Authorization');\n // Only advertise credentials when we echo back a specific origin (not '*')\n if (allowedOrigin && allowedOrigin !== '*') {\n headers.set('Access-Control-Allow-Credentials', 'true');\n }\n}\n\nfunction normalizeEmailAddress(input: string): string {\n const trimmed = String(input || '').trim();\n const angleStart = trimmed.indexOf('<');\n const angleEnd = trimmed.indexOf('>');\n if (angleStart !== -1 && angleEnd > angleStart) {\n return trimmed.slice(angleStart + 1, angleEnd).trim().toLowerCase();\n }\n return trimmed.toLowerCase();\n}\n\nfunction normalizeRejectReason(input: unknown): string {\n return String(input || '')\n .replace(/[\\r\\n]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction toLowercaseHeaderRecord(input: unknown): Record<string, string> {\n const out: Record<string, string> = {};\n if (!input) return out;\n\n const maybeHeaders = input as any;\n if (typeof maybeHeaders.forEach === 'function') {\n try {\n maybeHeaders.forEach((v: unknown, k: unknown) => {\n out[String(k).toLowerCase()] = String(v);\n });\n return out;\n } catch { }\n }\n\n if (typeof maybeHeaders[Symbol.iterator] === 'function') {\n try {\n for (const entry of maybeHeaders as Iterable<unknown>) {\n if (!Array.isArray(entry)) continue;\n const [k, v] = entry as any[];\n out[String(k).toLowerCase()] = String(v);\n }\n return out;\n } catch { }\n }\n\n if (typeof input === 'object') {\n for (const [k, v] of Object.entries(input as Record<string, unknown>)) {\n out[String(k).toLowerCase()] = String(v);\n }\n }\n\n return out;\n}\n\nasync function buildForwardableEmailPayloadFromCloudflareMessage(message: CfEmailMessage): Promise<{\n from: string;\n to: string;\n headers: Record<string, string>;\n raw: string;\n rawSize?: number;\n}> {\n const from = String(message?.from || '');\n const to = String(message?.to || '');\n const headers = toLowercaseHeaderRecord((message as any)?.headers);\n\n let raw = '';\n try {\n raw = await new Response((message as any)?.raw).text();\n } catch { }\n\n const rawSize = typeof (message as any)?.rawSize === 'number' ? (message as any).rawSize : undefined;\n\n return { from, to, headers, raw, rawSize };\n}\n\nexport interface CloudflareEmailHandlerOptions {\n /**\n * Optional recipient allowlist for emails processed by this handler (case-insensitive).\n * If unset, any `to` is accepted.\n */\n expectedRecipient?: string;\n /**\n * If true (default), unexpected recipients only log a warning (Cloudflare routing\n * is usually already scoped). If false, the handler rejects the email.\n */\n allowUnexpectedRecipient?: boolean;\n /**\n * When true, logs email metadata (from/to/subject). Defaults to false.\n */\n verbose?: boolean;\n // Optional logger; defaults to silent.\n logger?: RouterLogger | null;\n}\n\nexport function createCloudflareEmailHandler(service: AuthService, opts: CloudflareEmailHandlerOptions = {}): EmailHandler {\n const logger = normalizeRouterLogger(opts.logger);\n const expectedRecipient = normalizeEmailAddress(opts.expectedRecipient || '');\n const allowUnexpectedRecipient = opts.allowUnexpectedRecipient !== false;\n const verbose = Boolean(opts.verbose);\n\n return async (message: CfEmailMessage): Promise<void> => {\n try {\n const payload = await buildForwardableEmailPayloadFromCloudflareMessage(message);\n\n const to = normalizeEmailAddress(payload.to);\n if (expectedRecipient) {\n if (to !== expectedRecipient) {\n logger.warn('[email] unexpected recipient', { to, expectedRecipient });\n if (!allowUnexpectedRecipient) {\n message.setReject('Email recovery relayer rejected email: unexpected recipient');\n return;\n }\n }\n }\n\n if (verbose) {\n logger.info('[email] from/to', { from: payload.from, to: payload.to, subject: payload.headers['subject'] });\n }\n\n const parsed = parseRecoverEmailRequest(payload as any, { headers: payload.headers });\n if (!parsed.ok) {\n logger.warn('[email] rejecting', { code: parsed.code, message: parsed.message });\n message.setReject(`Email recovery relayer rejected email: ${parsed.message}`);\n return;\n }\n\n if (!service.emailRecovery) {\n logger.warn('[email] rejecting: EmailRecoveryService not configured');\n message.setReject('Email recovery relayer rejected email: email recovery service unavailable');\n return;\n }\n\n const result = await service.emailRecovery.requestEmailRecovery({\n accountId: parsed.accountId,\n emailBlob: parsed.emailBlob,\n explicitMode: parsed.explicitMode,\n });\n\n if (!result?.success) {\n logger.warn('[email] recovery failed', {\n accountId: parsed.accountId,\n error: result?.error || 'unknown',\n message: result?.message,\n });\n const reason = normalizeRejectReason(result?.message || result?.error || 'recovery failed');\n message.setReject(`Email recovery relayer rejected email: ${reason}`);\n return;\n }\n\n logger.info('[email] recovery submitted', { accountId: parsed.accountId });\n } catch (e: any) {\n logger.error('[email] internal error', { message: e?.message || String(e) });\n message.setReject('Email recovery relayer rejected email: internal error');\n }\n };\n}\n\nexport function createCloudflareRouter(service: AuthService, opts: RelayRouterOptions = {}): FetchHandler {\n const notFound = () => new Response('Not Found', { status: 404 });\n const mePath = opts.sessionRoutes?.auth || '/session/auth';\n const logoutPath = opts.sessionRoutes?.logout || '/session/logout';\n const logger = normalizeRouterLogger(opts.logger);\n const signedDelegatePath = (() => {\n if (!opts.signedDelegate) return '';\n const raw = String(opts.signedDelegate.route || '').trim();\n if (!raw) throw new Error('RelayRouterOptions.signedDelegate.route is required');\n return normalizePath(raw);\n })();\n const signedDelegatePolicy = opts.signedDelegate?.policy;\n\n return async function handler(request: Request, env?: CfEnv, ctx?: CfExecutionContext): Promise<Response> {\n const url = new URL(request.url);\n const { pathname } = url;\n const method = request.method.toUpperCase();\n\n // Preflight CORS\n if (method === 'OPTIONS') {\n const res = new Response(null, { status: 204 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n // Helper to adapt AuthService HTTP-like handlers to Response\n const toResponse = (out: { status: number; headers: Record<string, string>; body: string }) => {\n const res = new Response(out.body, { status: out.status, headers: out.headers });\n withCors(res.headers, opts, request);\n return res;\n };\n\n const isObject = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null;\n\n try {\n // ROR well-known manifest; allow override via env (optional)\n if (method === 'GET' && (pathname === '/.well-known/webauthn' || pathname === '/.well-known/webauthn/')) {\n const contractId = (env?.ROR_CONTRACT_ID || env?.WEBAUTHN_CONTRACT_ID || '').toString().trim() || undefined;\n const methodName = (env?.ROR_METHOD || '').toString().trim() || undefined;\n const origins = await service.getRorOrigins({ contractId, method: methodName });\n const res = json({ origins }, { status: 200, headers: { 'Cache-Control': 'max-age=60, stale-while-revalidate=600' } });\n withCors(res.headers, opts, request);\n return res;\n }\n\n if (method === 'POST' && pathname === '/create_account_and_register_user') {\n let body: unknown;\n try { body = await request.json(); } catch { body = null; }\n if (!isObject(body)) {\n const res = json({ code: 'invalid_body', message: 'JSON body required' }, { status: 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n const new_account_id = typeof body.new_account_id === 'string' ? body.new_account_id : '';\n const new_public_key = typeof body.new_public_key === 'string' ? body.new_public_key : '';\n const vrf_data = isObject(body.vrf_data) ? body.vrf_data : null;\n const webauthn_registration = isObject(body.webauthn_registration) ? body.webauthn_registration : null;\n const deterministic_vrf_public_key = (body as Record<string, unknown>).deterministic_vrf_public_key;\n const authenticator_options = isObject((body as Record<string, unknown>).authenticator_options)\n ? (body as Record<string, unknown>).authenticator_options\n : undefined;\n\n if (!new_account_id) {\n const res = json({ code: 'invalid_body', message: 'Missing or invalid new_account_id' }, { status: 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n if (!new_public_key) {\n const res = json({ code: 'invalid_body', message: 'Missing or invalid new_public_key' }, { status: 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n if (!vrf_data) {\n const res = json({ code: 'invalid_body', message: 'Missing or invalid vrf_data' }, { status: 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n if (!webauthn_registration) {\n const res = json({ code: 'invalid_body', message: 'Missing or invalid webauthn_registration' }, { status: 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n const input = {\n new_account_id,\n new_public_key,\n vrf_data,\n webauthn_registration,\n deterministic_vrf_public_key,\n authenticator_options,\n } as unknown as CreateAccountAndRegisterRequest;\n\n const result = await service.createAccountAndRegisterUser(input);\n const res = json(result, { status: result.success ? 200 : 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n // SignedDelegate meta-tx submission (optional)\n if (signedDelegatePath && pathname === signedDelegatePath) {\n if (method === 'OPTIONS') {\n const res = new Response(null, { status: 204 });\n withCors(res.headers, opts, request);\n return res;\n }\n if (method !== 'POST') return notFound();\n\n let body: unknown;\n try { body = await request.json(); } catch { body = null; }\n const valid = isObject(body) && typeof (body as any).hash === 'string' && Boolean((body as any).hash) && Boolean((body as any).signedDelegate);\n if (!valid) {\n const res = json({ ok: false, code: 'invalid_body', message: 'Expected { hash, signedDelegate }' }, { status: 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n const result = await service.executeSignedDelegate({\n hash: String((body as any).hash),\n signedDelegate: (body as any).signedDelegate,\n policy: signedDelegatePolicy,\n });\n\n if (!result || !result.ok) {\n const res = json({\n ok: false,\n code: result?.code || 'delegate_execution_failed',\n message: result?.error || 'Failed to execute delegate action',\n }, { status: 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n const res = json({\n ok: true,\n relayerTxHash: result.transactionHash || null,\n status: 'submitted',\n outcome: result.outcome ?? null,\n }, { status: 200 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n if (method === 'POST' && pathname === '/vrf/apply-server-lock') {\n const shamir = service.shamirService;\n if (!shamir || !(await shamir.ensureReady())) {\n const res = json({ code: 'shamir_disabled', message: 'Shamir 3-pass is not configured on this server' }, { status: 503 });\n withCors(res.headers, opts, request);\n return res;\n }\n let body: unknown; try { body = await request.json(); } catch { body = null; }\n const valid = isObject(body) && typeof body.kek_c_b64u === 'string' && body.kek_c_b64u.length > 0;\n if (!valid) {\n const res = json({ code: 'invalid_body', message: 'kek_c_b64u is required' }, { status: 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n const out = await handleApplyServerLock(shamir, {\n body: { kek_c_b64u: String((body as Record<string, unknown>).kek_c_b64u) },\n });\n return toResponse(out);\n }\n\n if (method === 'POST' && pathname === '/verify-authentication-response') {\n let body: unknown; try { body = await request.json(); } catch { body = null; }\n const valid = isObject(body)\n && isObject((body as any).vrf_data)\n && isObject((body as any).webauthn_authentication);\n if (!valid) {\n const res = json({ code: 'invalid_body', message: 'vrf_data and webauthn_authentication are required' }, { status: 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n try {\n const sessionKind = ((body as any)?.sessionKind || (body as any)?.session_kind) === 'cookie' ? 'cookie' : 'jwt';\n const result = await service.verifyAuthenticationResponse(body as any);\n const status = result.success ? 200 : 400;\n if (status !== 200) {\n const res = json({ code: 'not_verified', message: result.message || 'Authentication verification failed' }, { status });\n withCors(res.headers, opts, request);\n return res;\n }\n const res = json(result, { status: 200 });\n const session = opts.session;\n if (session && result.verified) {\n try {\n const userId = String((body as any).vrf_data?.user_id || '');\n const token = await session.signJwt(userId, { rpId: (body as any).vrf_data?.rp_id, blockHeight: (body as any).vrf_data?.block_height });\n logger.info(`[relay] creating ${sessionKind === 'cookie' ? 'HttpOnly session' : 'JWT'} for`, userId);\n if (sessionKind === 'cookie') {\n res.headers.set('Set-Cookie', session.buildSetCookie(token));\n } else {\n const payload = await res.clone().json();\n return new Response(JSON.stringify({ ...payload, jwt: token }), { status: 200, headers: res.headers });\n }\n } catch {}\n }\n withCors(res.headers, opts, request);\n return res;\n } catch (e: any) {\n const res = json({ code: 'internal', message: e?.message || 'Internal error' }, { status: 500 });\n withCors(res.headers, opts, request);\n return res;\n }\n }\n\n if (method === 'GET' && pathname === mePath) {\n try {\n const headersObj: Record<string, string> = {};\n request.headers.forEach((v, k) => { headersObj[k] = v; });\n const session = opts.session;\n if (!session) {\n const res = json({ authenticated: false, code: 'sessions_disabled', message: 'Sessions are not configured' }, { status: 501 });\n withCors(res.headers, opts, request);\n return res;\n }\n const parsed = await session.parse(headersObj);\n const res = json(parsed.ok ? { authenticated: true, claims: (parsed as any).claims } : { authenticated: false }, { status: parsed.ok ? 200 : 401 });\n withCors(res.headers, opts, request);\n return res;\n } catch (e: any) {\n const res = json({ authenticated: false, code: 'internal', message: e?.message || 'Internal error' }, { status: 500 });\n withCors(res.headers, opts, request);\n return res;\n }\n }\n\n if (method === 'POST' && pathname === logoutPath) {\n const res = json({ success: true }, { status: 200 });\n const session = opts.session;\n if (session) {\n // Clear cookie with Max-Age=0\n res.headers.set('Set-Cookie', session.buildClearCookie());\n }\n withCors(res.headers, opts, request);\n return res;\n }\n\n if (method === 'POST' && pathname === '/session/refresh') {\n let body: unknown; try { body = await request.json(); } catch { body = null; }\n const sessionKind = ((body as any)?.sessionKind || (body as any)?.session_kind) === 'cookie' ? 'cookie' : 'jwt';\n const session = opts.session;\n if (!session) {\n const res = json({ code: 'sessions_disabled', message: 'Sessions are not configured' }, { status: 501 });\n withCors(res.headers, opts, request);\n return res;\n }\n const out = await session.refresh(Object.fromEntries(request.headers.entries()));\n if (!out.ok || !out.jwt) {\n const res = json({ code: out.code || 'not_eligible', message: out.message || 'Refresh not eligible' }, { status: (out.code === 'unauthorized') ? 401 : 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n const res = json(sessionKind === 'cookie' ? { ok: true } : { ok: true, jwt: out.jwt }, { status: 200 });\n if (sessionKind === 'cookie' && out.jwt) {\n try {\n res.headers.set('Set-Cookie', session.buildSetCookie(out.jwt));\n } catch {}\n }\n withCors(res.headers, opts, request);\n return res;\n }\n\n if (method === 'POST' && pathname === '/recover-email') {\n const prefer = String(request.headers.get('prefer') || '').toLowerCase();\n const respondAsync =\n prefer.includes('respond-async') ||\n String(url.searchParams.get('async') || '').trim() === '1' ||\n String(url.searchParams.get('respond_async') || '').trim() === '1';\n\n let rawBody: unknown; try { rawBody = await request.json(); } catch { rawBody = null; }\n const parsed = parseRecoverEmailRequest(rawBody, { headers: request.headers });\n if (!parsed.ok) {\n const res = json({ code: parsed.code, message: parsed.message }, { status: parsed.status });\n withCors(res.headers, opts, request);\n return res;\n }\n const { accountId, emailBlob, explicitMode } = parsed;\n\n if (!service.emailRecovery) {\n const res = json(\n { code: 'email_recovery_unavailable', message: 'EmailRecoveryService is not configured on this server' },\n { status: 503 }\n );\n withCors(res.headers, opts, request);\n return res;\n }\n\n if (respondAsync && ctx && typeof ctx.waitUntil === 'function') {\n ctx.waitUntil(\n service.emailRecovery\n .requestEmailRecovery({ accountId, emailBlob, explicitMode })\n .then((result) => {\n logger.info('[recover-email] async complete', {\n success: result?.success === true,\n accountId,\n error: result?.success ? undefined : result?.error,\n });\n })\n .catch((err: any) => {\n logger.error('[recover-email] async error', {\n accountId,\n error: err?.message || String(err),\n });\n })\n );\n const res = json({ success: true, queued: true, accountId }, { status: 202 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n const result = await service.emailRecovery.requestEmailRecovery({ accountId, emailBlob, explicitMode });\n const res = json(result, { status: result.success ? 202 : 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n if (method === 'POST' && pathname === '/vrf/remove-server-lock') {\n const shamir = service.shamirService;\n if (!shamir || !(await shamir.ensureReady())) {\n const res = json({ code: 'shamir_disabled', message: 'Shamir 3-pass is not configured on this server' }, { status: 503 });\n withCors(res.headers, opts, request);\n return res;\n }\n let body: unknown; try { body = await request.json(); } catch { body = null; }\n const valid = isObject(body)\n && typeof body.kek_cs_b64u === 'string' && body.kek_cs_b64u.length > 0\n && typeof (body as Record<string, unknown>).keyId === 'string'\n && String((body as Record<string, unknown>).keyId).length > 0;\n if (!valid) {\n const res = json({ code: 'invalid_body', message: 'kek_cs_b64u and keyId are required' }, { status: 400 });\n withCors(res.headers, opts, request);\n return res;\n }\n const out = await handleRemoveServerLock(shamir, {\n body: {\n kek_cs_b64u: String((body as Record<string, unknown>).kek_cs_b64u),\n keyId: String((body as Record<string, unknown>).keyId),\n },\n });\n return toResponse(out);\n }\n\n if (method === 'GET' && pathname === '/shamir/key-info') {\n const shamir = service.shamirService;\n if (!shamir || !(await shamir.ensureReady())) {\n const res = json({ code: 'shamir_disabled', message: 'Shamir 3-pass is not configured on this server' }, { status: 503 });\n withCors(res.headers, opts, request);\n return res;\n }\n const out = await handleGetShamirKeyInfo(shamir);\n return toResponse(out);\n }\n\n if (opts.healthz && method === 'GET' && pathname === '/healthz') {\n // Surface simple CORS info for diagnostics (normalized)\n const allowed = buildCorsOrigins(...(opts.corsOrigins || []));\n const corsAllowed = allowed === '*' ? '*' : allowed;\n const shamir = service.shamirService;\n const shamirConfigured = Boolean(shamir && shamir.hasShamir());\n let currentKeyId: string | null = null;\n if (shamirConfigured && shamir) {\n try {\n const { currentKeyId: id } = JSON.parse((await handleGetShamirKeyInfo(shamir)).body) as { currentKeyId?: string };\n currentKeyId = id || null;\n } catch {}\n }\n\n const proverBaseUrl = service.emailRecovery?.getZkEmailProverBaseUrl?.() ?? null;\n const zkEmailConfigured = Boolean(proverBaseUrl);\n\n const res = json({\n ok: true,\n // Backwards-compatible field (was previously top-level).\n currentKeyId,\n shamir: { configured: shamirConfigured, currentKeyId },\n zkEmail: { configured: zkEmailConfigured, proverBaseUrl },\n cors: { allowedOrigins: corsAllowed },\n }, { status: 200 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n if (opts.readyz && method === 'GET' && pathname === '/readyz') {\n const allowed = buildCorsOrigins(...(opts.corsOrigins || []));\n const corsAllowed = allowed === '*' ? '*' : allowed;\n\n const shamir = service.shamirService;\n const shamirConfigured = Boolean(shamir && shamir.hasShamir());\n\n let shamirReady: boolean | null = null;\n let shamirCurrentKeyId: string | null = null;\n let shamirError: string | undefined;\n if (shamirConfigured && shamir) {\n try {\n await shamir.ensureReady();\n shamirReady = true;\n const { currentKeyId } = JSON.parse((await handleGetShamirKeyInfo(shamir)).body) as { currentKeyId?: string };\n shamirCurrentKeyId = currentKeyId || null;\n } catch (e: any) {\n shamirReady = false;\n shamirError = e?.message || String(e);\n }\n }\n\n const zk = service.emailRecovery\n ? await service.emailRecovery.checkZkEmailProverHealth()\n : { configured: false, baseUrl: null, healthy: null as boolean | null };\n\n const ok =\n (shamirConfigured ? shamirReady === true : true) &&\n (zk.configured ? zk.healthy === true : true);\n\n const res = json({\n ok,\n shamir: {\n configured: shamirConfigured,\n ready: shamirConfigured ? shamirReady : null,\n currentKeyId: shamirCurrentKeyId,\n error: shamirError,\n },\n zkEmail: zk,\n cors: { allowedOrigins: corsAllowed },\n }, { status: ok ? 200 : 503 });\n withCors(res.headers, opts, request);\n return res;\n }\n\n return notFound();\n } catch (e: unknown) {\n const res = json({ code: 'internal', message: e instanceof Error ? e.message : String(e) }, { status: 500 });\n withCors(res.headers, opts, request);\n return res;\n }\n };\n}\n\n/**\n * Optional cron hook factory for Cloudflare Workers.\n * Default is inactive (no-op). Enable explicitly in your Worker entry.\n *\n * Example:\n * const cron = createCloudflareCron(service, { enabled: env.ENABLE_ROTATION === '1', rotate: false });\n * export default { fetch: router, scheduled: cron };\n */\nexport interface CloudflareCronOptions {\n enabled?: boolean; // default false\n rotate?: boolean; // if true, will attempt to rotate Shamir keypair (not persisted in Workers)\n // Optional logger; defaults to silent.\n logger?: RouterLogger | null;\n}\n\nexport function createCloudflareCron(service: AuthService, opts: CloudflareCronOptions = {}): ScheduledHandler {\n const logger = normalizeRouterLogger(opts.logger);\n const enabled = Boolean(opts.enabled);\n const doRotate = Boolean(opts.rotate);\n if (!enabled) {\n return async () => { /* no-op by default */ };\n }\n return async (_event: CfScheduledEvent) => {\n try {\n if (doRotate) {\n // Rotation in Workers is ephemeral unless you persist keys externally.\n // This call rotates in-memory only and logs the result.\n const shamir = service.shamirService;\n if (!shamir) {\n logger.warn('[cloudflare-cron] Shamir not configured; skipping rotation');\n } else {\n const rotation = await shamir.rotateShamirServerKeypair();\n logger.info('[cloudflare-cron] rotated key', rotation.newKeyId, 'graceIds:', rotation.graceKeyIds);\n }\n } else {\n logger.info('[cloudflare-cron] enabled but rotate=false (no action)');\n }\n } catch (e: unknown) {\n logger.error('[cloudflare-cron] failed', e instanceof Error ? e.message : String(e));\n }\n };\n}\n"],"mappings":"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"}
|