@frak-labs/core-sdk 0.0.19-beta.f259d7fc → 0.1.0-beta.afa252b0

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/bundle.js CHANGED
@@ -10,4 +10,4 @@ import{Deferred as e,FrakRpcError as t,RpcErrorCodes as n,compressJson as r,crea
10
10
  Last Response: ${t.lastResponse}
11
11
  Client Status: ${t.clientStatus}
12
12
  Error: ${t.error}
13
- `.trim()}}let k={id:"frak-wallet",name:"frak-wallet",title:"Frak Wallet",allow:"publickey-credentials-get *; clipboard-write; web-share *",style:{width:"0",height:"0",border:"0",position:"absolute",zIndex:2000001,top:"-1000px",left:"-1000px",colorScheme:"auto"}};function b({walletBaseUrl:e,config:t}){let n=document.querySelector("#frak-wallet");n&&n.remove();let r=document.createElement("iframe");return r.id=k.id,r.name=k.name,r.allow=k.allow,r.style.zIndex=k.style.zIndex.toString(),S({iframe:r,isVisible:!1}),document.body.appendChild(r),new Promise(n=>{r?.addEventListener("load",()=>n(r)),r.src=`${t?.walletUrl??e??"https://wallet.frak.id"}/listener`})}function S({iframe:e,isVisible:t}){if(!t){e.style.width="0",e.style.height="0",e.style.border="0",e.style.position="fixed",e.style.top="-1000px",e.style.left="-1000px";return}e.style.position="fixed",e.style.top="0",e.style.left="0",e.style.width="100%",e.style.height="100%",e.style.pointerEvents="auto"}function I({config:r,iframe:o}){let c,l=r?.walletUrl??"https://wallet.frak.id",u=function({iframe:t}){let n=new e;return{handleEvent:async e=>{if(!("iframeLifecycle"in e))return;let{iframeLifecycle:r,data:a}=e;switch(r){case"connected":n.resolve(!0);break;case"do-backup":a.backup?localStorage.setItem(g,a.backup):localStorage.removeItem(g);break;case"remove-backup":localStorage.removeItem(g);break;case"show":case"hide":S({iframe:t,isVisible:"show"===r});break;case"handshake":t.contentWindow?.postMessage({clientLifecycle:"handshake-response",data:{token:a.token,currentUrl:window.location.href}},"*");break;case"redirect":{let e=new URL(a.baseRedirectUrl);e.searchParams.has("u")?(e.searchParams.delete("u"),e.searchParams.append("u",window.location.href),window.location.href=e.toString()):window.location.href=a.baseRedirectUrl}}},isConnected:n.promise}}({iframe:o}),d=new y(r,o);if(!o.contentWindow)throw new t(n.configError,"The iframe does not have a content window");let f=a({emittingTransport:o.contentWindow,listeningTransport:window,targetOrigin:l,middleware:[{async onRequest(e,r){if(!await u.isConnected)throw new t(n.clientNotConnected,"The iframe provider isn't connected yet");return r}},i(),{onRequest:(e,t)=>(d.setLastRequest(e),t),onResponse:(e,t)=>(d.setLastResponse(e,t),t)}],lifecycleHandlers:{iframeLifecycle:async(e,t)=>{await u.handleEvent(e)}}}),p=function(e,t){let n,r,a=()=>e.sendLifecycle({clientLifecycle:"heartbeat"});function o(){n&&clearInterval(n),r&&clearTimeout(r)}return async function(){a(),n=setInterval(a,1e3),r=setTimeout(()=>{o(),console.log("Heartbeat timeout: connection failed")},3e4),await t.isConnected,o()}(),o}(f,u),m=async()=>{p(),f.cleanup(),o.remove()};console.log("[Frak SDK] Initializing OpenPanel"),(c=new s({apiUrl:"https://op-api.gcp.frak.id",clientId:"6eacc8d7-49ac-4936-95e9-81ef29449570",trackScreenViews:!0,trackOutgoingLinks:!0,trackAttributes:!1,filter:({type:e,payload:t})=>!("track"===e&&t?.properties)||("sdkVersion"in t.properties||(t.properties={...t.properties,sdkVersion:"0.0.19"}),!0)})).setGlobalProperties({sdkVersion:"0.0.19"}),c.init();let w=v({config:r,rpcClient:f,lifecycleManager:u}).then(()=>d.updateSetupStatus(!0));return{config:r,debugInfo:d,waitForConnection:u.isConnected,waitForSetup:w,request:f.request,listenerRequest:f.listen,destroy:m,openPanel:c}}async function v({config:e,rpcClient:t,lifecycleManager:n}){async function r(){let n=e.customizations?.css;n&&t.sendLifecycle({clientLifecycle:"modal-css",data:{cssLink:n}})}async function a(){let n=e.customizations?.i18n;n&&t.sendLifecycle({clientLifecycle:"modal-i18n",data:{i18n:n}})}async function o(){if("undefined"==typeof window)return;let e=window.localStorage.getItem(g);e&&t.sendLifecycle({clientLifecycle:"restore-backup",data:{backup:e}})}await n.isConnected,function(e,t){if("undefined"==typeof window)return;let n=new URL(window.location.href),r=n.searchParams.get("sso");r&&(t.then(()=>{e.sendLifecycle({clientLifecycle:"sso-redirect-complete",data:{compressed:r}}),console.log("[SSO URL Listener] Forwarded compressed SSO data to iframe")}).catch(e=>{console.error("[SSO URL Listener] Failed to forward SSO data:",e)}),n.searchParams.delete("sso"),window.history.replaceState({},"",n.toString()),console.log("[SSO URL Listener] SSO parameter detected and URL cleaned"))}(t,n.isConnected),await Promise.allSettled([r(),a(),o()])}let R={eur:"fr-FR",usd:"en-US",gbp:"en-GB"};function L(e){return e&&e in R?e:"eur"}async function x({config:e}){let t=function(e){let t=L(e.metadata?.currency);return{...e,metadata:{...e.metadata,currency:t}}}(e),n=await b({config:t});if(!n)return void console.error("Failed to create iframe");let r=I({config:t,iframe:n});return(await r.waitForSetup,await r.waitForConnection)?r:void console.error("Failed to connect to client")}function F(e){return btoa(Array.from(e,e=>String.fromCharCode(e)).join("")).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function C(e){let t=e.length%4;return Uint8Array.from(atob(e.replace(/-/g,"+").replace(/_/g,"/").padEnd(e.length+(0===t?0:4-t),"=")),e=>e.charCodeAt(0))}function T(e){return F(r(e))}function E(e){return o(C(e))}let D="fCtx";function P(e){if(e?.r)try{let t=d(e.r);return F(t)}catch(t){console.error("Error compressing Frak context",{e:t,context:e})}}function U(e){if(e&&0!==e.length)try{let t=C(e);return{r:c(t,{size:20})}}catch(t){console.error("Error decompressing Frak context",{e:t,context:e})}}function q({url:e}){if(!e)return null;let t=new URL(e).searchParams.get(D);return t?U(t):null}function A({url:e,context:t}){if(!e)return null;let n=q({url:e}),r=n?{...n,...t}:t;if(!r.r)return null;let a=P(r);if(!a)return null;let o=new URL(e);return o.searchParams.set(D,a),o.toString()}function $(e){let t=new URL(e);return t.searchParams.delete(D),t.toString()}let O={compress:P,decompress:U,parse:q,update:A,remove:$,replaceUrl:function({url:e,context:t}){let n;if(!window.location?.href||"undefined"==typeof window)return void console.error("No window found, can't update context");let r=e??window.location.href;(n=null!==t?A({url:r,context:t}):$(r))&&window.history.replaceState(null,"",n.toString())}};function W(e){return e?R[e]??R.eur:R.eur}function z(e){return e?`${e}Amount`:"eurAmount"}function N(e,t){let n=W(t),r=L(t);return e.toLocaleString(n,{style:"currency",currency:r,minimumFractionDigits:0,maximumFractionDigits:2})}function _(e,t,n={}){if(!e)return void console.debug("[Frak] No client provided, skipping event tracking");try{e.openPanel?.track(t,n)}catch(e){console.debug("[Frak] Failed to track event:",t,e)}}let M={dapp:1,press:2,webshop:3,retail:4,referral:30,purchase:31},V=Object.entries(M).reduce((e,[t,n])=>(e[t]=BigInt(1)<<BigInt(n),e),{}),B={press:{openArticle:"0xc0a24ffb",readArticle:"0xd5bd0fbe"},dapp:{proofVerifiableStorageUpdate:"0x2ab2aeef",callableVerifiableStorageUpdate:"0xa07da986"},webshop:{open:"0xb311798f"},referral:{referred:"0x010cc3b9",createLink:"0xb2c0f17c"},purchase:{started:"0xd87e90c3",completed:"0x8403aeb4",unsafeCompleted:"0x4d5b14e0"},retail:{customerMeeting:"0x74489004"}};function j(t,n){if(!n)return t.request({method:"frak_listenToWalletStatus"}).then(e=>(G(t,e),e));let r=new e,a=!1;return t.listenerRequest({method:"frak_listenToWalletStatus"},e=>{G(t,e),n(e),a||(r.resolve(e),a=!0)}),r.promise}function G(e,t){"undefined"!=typeof window&&(e.openPanel?.setGlobalProperties({wallet:t.wallet??null}),t.interactionToken?window.sessionStorage.setItem("frak-wallet-interaction-token",t.interactionToken):window.sessionStorage.removeItem("frak-wallet-interaction-token"))}async function J(e,{productId:t,interaction:n,validation:r}){let a=t??function({domain:e}){return p(w((e??window.location.host).replace("www.","")))}(e.config);return await e.request({method:"frak_sendInteraction",params:[a,n,r]})}async function H(e,{steps:t,metadata:n}){return await e.request({method:"frak_displayModal",params:[t,n,e.config.metadata]})}async function K(e,t){return await e.request({method:"frak_displayEmbeddedWallet",params:[t,e.config.metadata]})}async function Q(e,t){let{metadata:n,customizations:r}=e.config;return await e.request({method:"frak_sso",params:[t,n.name,r?.css]})??{}}async function X(e){return await e.request({method:"frak_getProductInformation"})}async function Y(e){if("undefined"==typeof window)return void console.warn("[Frak] No window found, can't track purchase");let t=window.sessionStorage.getItem("frak-wallet-interaction-token");if(!t)return void console.warn("[Frak] No frak session found, skipping purchase check");await fetch("https://backend.frak.id/interactions/listenForPurchase",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json","x-wallet-sdk-auth":t},body:JSON.stringify(e)})}async function Z(e,{siwe:t,metadata:n}){let r=e.config?.domain??window.location.host,a=t?.statement??`I confirm that I want to use my Frak wallet on: ${e.config.metadata.name}`,o={...t,statement:a,nonce:t?.nonce??h(),uri:t?.uri??`https://${r}`,version:t?.version??"1",domain:r};return(await H(e,{metadata:n,steps:{login:{},siweAuthenticate:{siwe:o}}})).siweAuthenticate}async function ee(e,{tx:t,metadata:n}){return(await H(e,{metadata:n,steps:{login:{},sendTransaction:{tx:t}}})).sendTransaction}function et(e,{metadata:t,login:n,openSession:r}){return function e(t,n){async function r(e){return e&&(n.metadata=e(n.metadata??{})),await H(t,n)}return{params:n,sendTx:function(r){return e(t,{...n,steps:{...n.steps,sendTransaction:r}})},reward:function(r){return e(t,{...n,steps:{...n.steps,final:{...r,action:{key:"reward"}}}})},sharing:function(r,a){return e(t,{...n,steps:{...n.steps,final:{...a,action:{key:"sharing",options:r}}}})},display:r}}(e,{steps:{login:n??{},openSession:r??{}},metadata:t})}let en={createLink:()=>({handlerTypeDenominator:w(M.referral),interactionData:B.referral.createLink}),referred({referrer:e}){let t=l([B.referral.referred,m(e,{size:32})]);return{handlerTypeDenominator:w(M.referral),interactionData:t}}};async function er(e,{walletStatus:r,frakContext:a,modalConfig:o,productId:i,options:s}){let c=!1;async function l(){if(!c)return c=!0,eo(e,{modalConfig:{...o,loggedIn:{action:{key:"referred"}}},walletStatus:r})}async function u(t){let n=en.referred({referrer:t});await Promise.allSettled([J(e,{productId:i,interaction:n}),_(e,"user_referred",{properties:{referrer:t}})])}try{let{status:e,currentWallet:t}=await ea({initialWalletStatus:r,getFreshWalletStatus:l,pushReferralInteraction:u,frakContext:a});return O.replaceUrl({url:window.location?.href,context:s?.alwaysAppendUrl?{r:t}:null}),e}catch(e){return console.log("Error processing referral",{error:e}),O.replaceUrl({url:window.location?.href,context:s?.alwaysAppendUrl?{r:r?.wallet}:null}),function(e){if(e instanceof t)switch(e.code){case n.walletNotConnected:return"no-wallet";case n.serverErrorForInteractionDelegation:return"no-session"}return"error"}(e)}}async function ea({initialWalletStatus:e,getFreshWalletStatus:t,pushReferralInteraction:n,frakContext:r}){let a=e?.wallet;return r?.r?(a||(a=await t()),a&&f(r.r,a))?{status:"self-referral",currentWallet:a}:(e?.interactionSession||(a=await t()),await n(r.r),{status:"success",currentWallet:a}):{status:"no-referrer",currentWallet:a}}async function eo(e,{modalConfig:t,walletStatus:n}){if(!n?.interactionSession){let n=await K(e,t??{});return n?.wallet??void 0}return n.wallet??void 0}async function ei(e,{productId:t,modalConfig:n,options:r}={}){let a=O.parse({url:window.location.href}),o=await j(e);try{return await er(e,{walletStatus:o,frakContext:a,modalConfig:n,productId:t,options:r})}catch(e){console.warn("Error processing referral",{error:e})}}let es={openArticle({articleId:e}){let t=l([B.press.openArticle,m(e,{size:32})]);return{handlerTypeDenominator:w(M.press),interactionData:t}},readArticle({articleId:e}){let t=l([B.press.readArticle,m(e,{size:32})]);return{handlerTypeDenominator:w(M.press),interactionData:t}}},ec={startPurchase({purchaseId:e}){let t=l([B.purchase.started,m(e,{size:32})]);return{handlerTypeDenominator:w(M.purchase),interactionData:t}},completedPurchase({purchaseId:e,proof:t}){let n=u([{type:"uint256"},{type:"bytes32[]"}],[BigInt(e),t]),r=l([B.purchase.completed,n]);return{handlerTypeDenominator:w(M.purchase),interactionData:r}},unsafeCompletedPurchase({purchaseId:e}){let t=l([B.purchase.unsafeCompleted,m(e,{size:32})]);return{handlerTypeDenominator:w(M.purchase),interactionData:t}}},el={open:()=>({handlerTypeDenominator:w(M.webshop),interactionData:B.webshop.open})},eu={customerMeeting({agencyId:e}){let t=l([B.retail.customerMeeting,m(e,{size:32})]);return{handlerTypeDenominator:w(M.retail),interactionData:t}}};export{y as DebugInfoGatherer,O as FrakContextManager,es as PressInteractionEncoder,ec as PurchaseInteractionEncoder,en as ReferralInteractionEncoder,eu as RetailInteractionEncoder,el as WebShopInteractionEncoder,C as base64urlDecode,F as base64urlEncode,k as baseIframeProps,T as compressJsonToB64,I as createIFrameFrakClient,b as createIframe,E as decompressJsonFromB64,K as displayEmbeddedWallet,H as displayModal,N as formatAmount,z as getCurrencyAmountKey,X as getProductInformation,L as getSupportedCurrency,W as getSupportedLocale,B as interactionTypes,R as locales,et as modalBuilder,Q as openSso,er as processReferral,M as productTypes,V as productTypesMask,ei as referralInteraction,J as sendInteraction,ee as sendTransaction,x as setupClient,Z as siweAuthenticate,_ as trackEvent,Y as trackPurchaseStatus,j as watchWalletStatus};
13
+ `.trim()}}let k={id:"frak-wallet",name:"frak-wallet",title:"Frak Wallet",allow:"publickey-credentials-get *; clipboard-write; web-share *",style:{width:"0",height:"0",border:"0",position:"absolute",zIndex:2000001,top:"-1000px",left:"-1000px",colorScheme:"auto"}};function S({walletBaseUrl:e,config:t}){let n=document.querySelector("#frak-wallet");n&&n.remove();let r=document.createElement("iframe");return r.id=k.id,r.name=k.name,r.allow=k.allow,r.style.zIndex=k.style.zIndex.toString(),b({iframe:r,isVisible:!1}),document.body.appendChild(r),new Promise(n=>{r?.addEventListener("load",()=>n(r)),r.src=`${t?.walletUrl??e??"https://wallet.frak.id"}/listener`})}function b({iframe:e,isVisible:t}){if(!t){e.style.width="0",e.style.height="0",e.style.border="0",e.style.position="fixed",e.style.top="-1000px",e.style.left="-1000px";return}e.style.position="fixed",e.style.top="0",e.style.left="0",e.style.width="100%",e.style.height="100%",e.style.pointerEvents="auto"}function I(e="/listener"){if(!window.opener)return null;let t=t=>{try{return t.location.origin===window.location.origin&&t.location.pathname===e}catch{return!1}};try{let e=window.opener.frames;for(let n=0;n<e.length;n++)if(t(e[n]))return e[n];return null}catch(t){return console.error(`[findIframeInOpener] Error finding iframe with pathname ${e}:`,t),null}}function L({config:r,iframe:o}){let c,l=r?.walletUrl??"https://wallet.frak.id",u=function({iframe:t}){let n=new e;return{handleEvent:async e=>{if(!("iframeLifecycle"in e))return;let{iframeLifecycle:r,data:a}=e;switch(r){case"connected":n.resolve(!0);break;case"do-backup":a.backup?localStorage.setItem(g,a.backup):localStorage.removeItem(g);break;case"remove-backup":localStorage.removeItem(g);break;case"show":case"hide":b({iframe:t,isVisible:"show"===r});break;case"handshake":t.contentWindow?.postMessage({clientLifecycle:"handshake-response",data:{token:a.token,currentUrl:window.location.href}},"*");break;case"redirect":{let e=new URL(a.baseRedirectUrl);e.searchParams.has("u")?(e.searchParams.delete("u"),e.searchParams.append("u",window.location.href),window.location.href=e.toString()):window.location.href=a.baseRedirectUrl}}},isConnected:n.promise}}({iframe:o}),d=new y(r,o);if(!o.contentWindow)throw new t(n.configError,"The iframe does not have a content window");let f=a({emittingTransport:o.contentWindow,listeningTransport:window,targetOrigin:l,middleware:[{async onRequest(e,r){if(!await u.isConnected)throw new t(n.clientNotConnected,"The iframe provider isn't connected yet");return r}},i(),{onRequest:(e,t)=>(d.setLastRequest(e),t),onResponse:(e,t)=>(d.setLastResponse(e,t),t)}],lifecycleHandlers:{iframeLifecycle:async(e,t)=>{await u.handleEvent(e)}}}),p=function(e,t){let n,r,a=()=>e.sendLifecycle({clientLifecycle:"heartbeat"});function o(){n&&clearInterval(n),r&&clearTimeout(r)}return async function(){a(),n=setInterval(a,1e3),r=setTimeout(()=>{o(),console.log("Heartbeat timeout: connection failed")},3e4),await t.isConnected,o()}(),o}(f,u),m=async()=>{p(),f.cleanup(),o.remove()};console.log("[Frak SDK] Initializing OpenPanel"),(c=new s({apiUrl:"https://op-api.gcp.frak.id",clientId:"6eacc8d7-49ac-4936-95e9-81ef29449570",trackScreenViews:!0,trackOutgoingLinks:!0,trackAttributes:!1,filter:({type:e,payload:t})=>!("track"===e&&t?.properties)||("sdkVersion"in t.properties||(t.properties={...t.properties,sdkVersion:"0.1.0"}),!0)})).setGlobalProperties({sdkVersion:"0.1.0"}),c.init();let w=v({config:r,rpcClient:f,lifecycleManager:u}).then(()=>d.updateSetupStatus(!0));return{config:r,debugInfo:d,waitForConnection:u.isConnected,waitForSetup:w,request:f.request,listenerRequest:f.listen,destroy:m,openPanel:c}}async function v({config:e,rpcClient:t,lifecycleManager:n}){async function r(){let n=e.customizations?.css;n&&t.sendLifecycle({clientLifecycle:"modal-css",data:{cssLink:n}})}async function a(){let n=e.customizations?.i18n;n&&t.sendLifecycle({clientLifecycle:"modal-i18n",data:{i18n:n}})}async function o(){if("undefined"==typeof window)return;let e=window.localStorage.getItem(g);e&&t.sendLifecycle({clientLifecycle:"restore-backup",data:{backup:e}})}await n.isConnected,function(e,t){if("undefined"==typeof window)return;let n=new URL(window.location.href),r=n.searchParams.get("sso");r&&(t.then(()=>{e.sendLifecycle({clientLifecycle:"sso-redirect-complete",data:{compressed:r}}),console.log("[SSO URL Listener] Forwarded compressed SSO data to iframe")}).catch(e=>{console.error("[SSO URL Listener] Failed to forward SSO data:",e)}),n.searchParams.delete("sso"),window.history.replaceState({},"",n.toString()),console.log("[SSO URL Listener] SSO parameter detected and URL cleaned"))}(t,n.isConnected),await Promise.allSettled([r(),a(),o()])}let R={eur:"fr-FR",usd:"en-US",gbp:"en-GB"};function U(e){return e&&e in R?e:"eur"}async function x({config:e}){let t=function(e){let t=U(e.metadata?.currency);return{...e,metadata:{...e.metadata,currency:t}}}(e),n=await S({config:t});if(!n)return void console.error("Failed to create iframe");let r=L({config:t,iframe:n});return(await r.waitForSetup,await r.waitForConnection)?r:void console.error("Failed to connect to client")}function E(e){return btoa(Array.from(e,e=>String.fromCharCode(e)).join("")).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function P(e){let t=e.length%4;return Uint8Array.from(atob(e.replace(/-/g,"+").replace(/_/g,"/").padEnd(e.length+(0===t?0:4-t),"=")),e=>e.charCodeAt(0))}function F(e){return E(r(e))}function C(e){return o(P(e))}let T="fCtx";function D(e){if(e?.r)try{let t=d(e.r);return E(t)}catch(t){console.error("Error compressing Frak context",{e:t,context:e})}}function q(e){if(e&&0!==e.length)try{let t=P(e);return{r:c(t,{size:20})}}catch(t){console.error("Error decompressing Frak context",{e:t,context:e})}}function A({url:e}){if(!e)return null;let t=new URL(e).searchParams.get(T);return t?q(t):null}function $({url:e,context:t}){if(!e)return null;let n=A({url:e}),r=n?{...n,...t}:t;if(!r.r)return null;let a=D(r);if(!a)return null;let o=new URL(e);return o.searchParams.set(T,a),o.toString()}function O(e){let t=new URL(e);return t.searchParams.delete(T),t.toString()}let W={compress:D,decompress:q,parse:A,update:$,remove:O,replaceUrl:function({url:e,context:t}){let n;if(!window.location?.href||"undefined"==typeof window)return void console.error("No window found, can't update context");let r=e??window.location.href;(n=null!==t?$({url:r,context:t}):O(r))&&window.history.replaceState(null,"",n.toString())}};function N(e){return e?R[e]??R.eur:R.eur}function z(e){return e?`${e}Amount`:"eurAmount"}function _(e,t){let n=N(t),r=U(t);return e.toLocaleString(n,{style:"currency",currency:r,minimumFractionDigits:0,maximumFractionDigits:2})}function M(e,t,n={}){if(!e)return void console.debug("[Frak] No client provided, skipping event tracking");try{e.openPanel?.track(t,n)}catch(e){console.debug("[Frak] Failed to track event:",t,e)}}function V(e,t,n,r,a){var o;let i=F({r:(o={redirectUrl:t.redirectUrl,directExit:t.directExit,lang:t.lang,productId:n,metadata:{name:r,css:a,logoUrl:t.metadata?.logoUrl,homepageLink:t.metadata?.homepageLink}}).redirectUrl,d:o.directExit,l:o.lang,p:o.productId,m:{n:o.metadata?.name,css:o.metadata?.css,l:o.metadata?.logoUrl,h:o.metadata?.homepageLink}}),s=new URL(e);return s.pathname="/sso",s.searchParams.set("p",i),s.toString()}let B={dapp:1,press:2,webshop:3,retail:4,referral:30,purchase:31},j=Object.entries(B).reduce((e,[t,n])=>(e[t]=BigInt(1)<<BigInt(n),e),{}),G={press:{openArticle:"0xc0a24ffb",readArticle:"0xd5bd0fbe"},dapp:{proofVerifiableStorageUpdate:"0x2ab2aeef",callableVerifiableStorageUpdate:"0xa07da986"},webshop:{open:"0xb311798f"},referral:{referred:"0x010cc3b9",createLink:"0xb2c0f17c"},purchase:{started:"0xd87e90c3",completed:"0x8403aeb4",unsafeCompleted:"0x4d5b14e0"},retail:{customerMeeting:"0x74489004"}};function J({domain:e}={}){return p(w((e??window.location.host).replace("www.","")))}let H="menubar=no,status=no,scrollbars=no,fullscreen=no,width=500, height=800",K="frak-sso";async function Q(e,t){let{metadata:n,customizations:r,walletUrl:a}=e.config;if(t.openInSameWindow??!!t.redirectUrl)return await e.request({method:"frak_openSso",params:[t,n.name,r?.css]});let o=t.ssoPopupUrl??V(a??"https://wallet.frak.id",t,J(),n.name,r?.css),i=window.open(o,K,H);if(!i)throw Error("Popup was blocked. Please allow popups for this site.");return i.focus(),await e.request({method:"frak_openSso",params:[t,n.name,r?.css]})??{}}function X(t,n){if(!n)return t.request({method:"frak_listenToWalletStatus"}).then(e=>(Y(t,e),e));let r=new e,a=!1;return t.listenerRequest({method:"frak_listenToWalletStatus"},e=>{Y(t,e),n(e),a||(r.resolve(e),a=!0)}),r.promise}function Y(e,t){"undefined"!=typeof window&&(e.openPanel?.setGlobalProperties({wallet:t.wallet??null}),t.interactionToken?window.sessionStorage.setItem("frak-wallet-interaction-token",t.interactionToken):window.sessionStorage.removeItem("frak-wallet-interaction-token"))}async function Z(e,{productId:t,interaction:n,validation:r}){let a=t??J(e.config);return await e.request({method:"frak_sendInteraction",params:[a,n,r]})}async function ee(e,{steps:t,metadata:n}){return await e.request({method:"frak_displayModal",params:[t,n,e.config.metadata]})}async function et(e,t){return await e.request({method:"frak_displayEmbeddedWallet",params:[t,e.config.metadata]})}async function en(e,t){let{metadata:n,customizations:r}=e.config;return await e.request({method:"frak_prepareSso",params:[t,n.name,r?.css]})}async function er(e){return await e.request({method:"frak_getProductInformation"})}async function ea(e){if("undefined"==typeof window)return void console.warn("[Frak] No window found, can't track purchase");let t=window.sessionStorage.getItem("frak-wallet-interaction-token");if(!t)return void console.warn("[Frak] No frak session found, skipping purchase check");await fetch("https://backend.frak.id/interactions/listenForPurchase",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json","x-wallet-sdk-auth":t},body:JSON.stringify(e)})}async function eo(e,{siwe:t,metadata:n}){let r=e.config?.domain??window.location.host,a=t?.statement??`I confirm that I want to use my Frak wallet on: ${e.config.metadata.name}`,o={...t,statement:a,nonce:t?.nonce??h(),uri:t?.uri??`https://${r}`,version:t?.version??"1",domain:r};return(await ee(e,{metadata:n,steps:{login:{},siweAuthenticate:{siwe:o}}})).siweAuthenticate}async function ei(e,{tx:t,metadata:n}){return(await ee(e,{metadata:n,steps:{login:{},sendTransaction:{tx:t}}})).sendTransaction}function es(e,{metadata:t,login:n,openSession:r}){return function e(t,n){async function r(e){return e&&(n.metadata=e(n.metadata??{})),await ee(t,n)}return{params:n,sendTx:function(r){return e(t,{...n,steps:{...n.steps,sendTransaction:r}})},reward:function(r){return e(t,{...n,steps:{...n.steps,final:{...r,action:{key:"reward"}}}})},sharing:function(r,a){return e(t,{...n,steps:{...n.steps,final:{...a,action:{key:"sharing",options:r}}}})},display:r}}(e,{steps:{login:n??{},openSession:r??{}},metadata:t})}let ec={createLink:()=>({handlerTypeDenominator:w(B.referral),interactionData:G.referral.createLink}),referred({referrer:e}){let t=l([G.referral.referred,m(e,{size:32})]);return{handlerTypeDenominator:w(B.referral),interactionData:t}}};async function el(e,{walletStatus:r,frakContext:a,modalConfig:o,productId:i,options:s}){let c=!1;async function l(){if(!c)return c=!0,ed(e,{modalConfig:{...o,loggedIn:{action:{key:"referred"}}},walletStatus:r})}async function u(t){let n=ec.referred({referrer:t});await Promise.allSettled([Z(e,{productId:i,interaction:n}),M(e,"user_referred",{properties:{referrer:t}})])}try{let{status:e,currentWallet:t}=await eu({initialWalletStatus:r,getFreshWalletStatus:l,pushReferralInteraction:u,frakContext:a});return W.replaceUrl({url:window.location?.href,context:s?.alwaysAppendUrl?{r:t}:null}),e}catch(e){return console.log("Error processing referral",{error:e}),W.replaceUrl({url:window.location?.href,context:s?.alwaysAppendUrl?{r:r?.wallet}:null}),function(e){if(e instanceof t)switch(e.code){case n.walletNotConnected:return"no-wallet";case n.serverErrorForInteractionDelegation:return"no-session"}return"error"}(e)}}async function eu({initialWalletStatus:e,getFreshWalletStatus:t,pushReferralInteraction:n,frakContext:r}){let a=e?.wallet;return r?.r?(a||(a=await t()),a&&f(r.r,a))?{status:"self-referral",currentWallet:a}:(e?.interactionSession||(a=await t()),await n(r.r),{status:"success",currentWallet:a}):{status:"no-referrer",currentWallet:a}}async function ed(e,{modalConfig:t,walletStatus:n}){if(!n?.interactionSession){let n=await et(e,t??{});return n?.wallet??void 0}return n.wallet??void 0}async function ef(e,{productId:t,modalConfig:n,options:r}={}){let a=W.parse({url:window.location.href}),o=await X(e);try{return await el(e,{walletStatus:o,frakContext:a,modalConfig:n,productId:t,options:r})}catch(e){console.warn("Error processing referral",{error:e})}}let ep={openArticle({articleId:e}){let t=l([G.press.openArticle,m(e,{size:32})]);return{handlerTypeDenominator:w(B.press),interactionData:t}},readArticle({articleId:e}){let t=l([G.press.readArticle,m(e,{size:32})]);return{handlerTypeDenominator:w(B.press),interactionData:t}}},em={startPurchase({purchaseId:e}){let t=l([G.purchase.started,m(e,{size:32})]);return{handlerTypeDenominator:w(B.purchase),interactionData:t}},completedPurchase({purchaseId:e,proof:t}){let n=u([{type:"uint256"},{type:"bytes32[]"}],[BigInt(e),t]),r=l([G.purchase.completed,n]);return{handlerTypeDenominator:w(B.purchase),interactionData:r}},unsafeCompletedPurchase({purchaseId:e}){let t=l([G.purchase.unsafeCompleted,m(e,{size:32})]);return{handlerTypeDenominator:w(B.purchase),interactionData:t}}},ew={open:()=>({handlerTypeDenominator:w(B.webshop),interactionData:G.webshop.open})},eh={customerMeeting({agencyId:e}){let t=l([G.retail.customerMeeting,m(e,{size:32})]);return{handlerTypeDenominator:w(B.retail),interactionData:t}}};export{y as DebugInfoGatherer,W as FrakContextManager,ep as PressInteractionEncoder,em as PurchaseInteractionEncoder,ec as ReferralInteractionEncoder,eh as RetailInteractionEncoder,ew as WebShopInteractionEncoder,P as base64urlDecode,E as base64urlEncode,k as baseIframeProps,F as compressJsonToB64,L as createIFrameFrakClient,S as createIframe,C as decompressJsonFromB64,et as displayEmbeddedWallet,ee as displayModal,I as findIframeInOpener,_ as formatAmount,V as generateSsoUrl,z as getCurrencyAmountKey,er as getProductInformation,U as getSupportedCurrency,N as getSupportedLocale,G as interactionTypes,R as locales,es as modalBuilder,Q as openSso,en as prepareSso,el as processReferral,B as productTypes,j as productTypesMask,ef as referralInteraction,Z as sendInteraction,ei as sendTransaction,x as setupClient,eo as siweAuthenticate,H as ssoPopupFeatures,K as ssoPopupName,M as trackEvent,ea as trackPurchaseStatus,X as watchWalletStatus};
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";let __rslib_import_meta_url__="undefined"==typeof document?new(require("url".replace("",""))).URL("file:"+__filename).href:document.currentScript&&document.currentScript.src||new URL("main.js",document.baseURI).href;var __webpack_require__={};__webpack_require__.d=(e,t)=>{for(var r in t)__webpack_require__.o(t,r)&&!__webpack_require__.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},__webpack_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),__webpack_require__.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var __webpack_exports__={};__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{decompressJsonFromB64:()=>decompressJsonFromB64,productTypesMask:()=>productTypesMask,interactionTypes:()=>interactionTypes,createIFrameFrakClient:()=>createIFrameFrakClient,getCurrencyAmountKey:()=>getCurrencyAmountKey,getSupportedCurrency:()=>getSupportedCurrency,FrakContextManager:()=>FrakContextManager,formatAmount:()=>formatAmount,getSupportedLocale:()=>getSupportedLocale,trackEvent:()=>trackEvent,compressJsonToB64:()=>compressJsonToB64,base64urlDecode:()=>base64urlDecode,baseIframeProps:()=>baseIframeProps,DebugInfoGatherer:()=>DebugInfoGatherer,base64urlEncode:()=>base64urlEncode,productTypes:()=>productTypes,setupClient:()=>setupClient,createIframe:()=>createIframe,locales:()=>locales});let frame_connector_namespaceObject=require("@frak-labs/frame-connector"),middleware_namespaceObject=require("@frak-labs/frame-connector/middleware"),web_namespaceObject=require("@openpanel/web"),BACKUP_KEY="nexus-wallet-backup";function setupSsoUrlListener(e,t){if("undefined"==typeof window)return;let r=new URL(window.location.href),a=r.searchParams.get("sso");a&&(t.then(()=>{e.sendLifecycle({clientLifecycle:"sso-redirect-complete",data:{compressed:a}}),console.log("[SSO URL Listener] Forwarded compressed SSO data to iframe")}).catch(e=>{console.error("[SSO URL Listener] Failed to forward SSO data:",e)}),r.searchParams.delete("sso"),window.history.replaceState({},"",r.toString()),console.log("[SSO URL Listener] SSO parameter detected and URL cleaned"))}class DebugInfoGatherer{config;iframe;isSetupDone=!1;lastResponse=null;lastRequest=null;constructor(e,t){this.config=e,this.iframe=t,this.lastRequest=null,this.lastResponse=null}setLastResponse(e,t){this.lastResponse={message:e,response:t,timestamp:Date.now()}}setLastRequest(e){this.lastRequest={event:e,timestamp:Date.now()}}updateSetupStatus(e){this.isSetupDone=e}base64Encode(e){try{return btoa(JSON.stringify(e))}catch(e){return console.warn("Failed to encode debug data",e),btoa("Failed to encode data")}}getIframeStatus(){return this.iframe?{loading:this.iframe.hasAttribute("loading"),url:this.iframe.src,readyState:this.iframe.contentDocument?.readyState?+("complete"===this.iframe.contentDocument.readyState):-1,contentWindow:!!this.iframe.contentWindow,isConnected:this.iframe.isConnected}:null}getNavigatorInfo(){return navigator?{userAgent:navigator.userAgent,language:navigator.language,onLine:navigator.onLine,screenWidth:window.screen.width,screenHeight:window.screen.height,pixelRatio:window.devicePixelRatio}:null}gatherDebugInfo(e){let t=this.getIframeStatus(),r=this.getNavigatorInfo(),a="Unknown";return e instanceof frame_connector_namespaceObject.FrakRpcError?a=`FrakRpcError: ${e.code} '${e.message}'`:e instanceof Error?a=e.message:"string"==typeof e&&(a=e),{timestamp:new Date().toISOString(),encodedUrl:btoa(window.location.href),encodedConfig:this.config?this.base64Encode(this.config):"no-config",navigatorInfo:r?this.base64Encode(r):"no-navigator",iframeStatus:t?this.base64Encode(t):"not-iframe",lastRequest:this.lastRequest?this.base64Encode(this.lastRequest):"No Frak request logged",lastResponse:this.lastResponse?this.base64Encode(this.lastResponse):"No Frak response logged",clientStatus:this.isSetupDone?"setup":"not-setup",error:a}}static empty(){return new DebugInfoGatherer}formatDebugInfo(e){let t=this.gatherDebugInfo(e);return`
1
+ "use strict";let __rslib_import_meta_url__="undefined"==typeof document?new(require("url".replace("",""))).URL("file:"+__filename).href:document.currentScript&&document.currentScript.src||new URL("main.js",document.baseURI).href;var __webpack_require__={};__webpack_require__.d=(e,t)=>{for(var r in t)__webpack_require__.o(t,r)&&!__webpack_require__.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},__webpack_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),__webpack_require__.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var __webpack_exports__={};__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{decompressJsonFromB64:()=>decompressJsonFromB64,productTypesMask:()=>productTypesMask,findIframeInOpener:()=>findIframeInOpener,createIFrameFrakClient:()=>createIFrameFrakClient,generateSsoUrl:()=>sso_generateSsoUrl,getCurrencyAmountKey:()=>getCurrencyAmountKey,getSupportedCurrency:()=>getSupportedCurrency,interactionTypes:()=>interactionTypes,FrakContextManager:()=>FrakContextManager,formatAmount:()=>formatAmount,getSupportedLocale:()=>getSupportedLocale,trackEvent:()=>trackEvent,compressJsonToB64:()=>compressJsonToB64,base64urlDecode:()=>base64urlDecode,baseIframeProps:()=>baseIframeProps,DebugInfoGatherer:()=>DebugInfoGatherer,base64urlEncode:()=>base64urlEncode,ssoPopupFeatures:()=>ssoPopupFeatures,productTypes:()=>productTypes,setupClient:()=>setupClient,createIframe:()=>createIframe,ssoPopupName:()=>ssoPopupName,locales:()=>locales});let frame_connector_namespaceObject=require("@frak-labs/frame-connector"),middleware_namespaceObject=require("@frak-labs/frame-connector/middleware"),web_namespaceObject=require("@openpanel/web"),BACKUP_KEY="nexus-wallet-backup";function setupSsoUrlListener(e,t){if("undefined"==typeof window)return;let r=new URL(window.location.href),o=r.searchParams.get("sso");o&&(t.then(()=>{e.sendLifecycle({clientLifecycle:"sso-redirect-complete",data:{compressed:o}}),console.log("[SSO URL Listener] Forwarded compressed SSO data to iframe")}).catch(e=>{console.error("[SSO URL Listener] Failed to forward SSO data:",e)}),r.searchParams.delete("sso"),window.history.replaceState({},"",r.toString()),console.log("[SSO URL Listener] SSO parameter detected and URL cleaned"))}class DebugInfoGatherer{config;iframe;isSetupDone=!1;lastResponse=null;lastRequest=null;constructor(e,t){this.config=e,this.iframe=t,this.lastRequest=null,this.lastResponse=null}setLastResponse(e,t){this.lastResponse={message:e,response:t,timestamp:Date.now()}}setLastRequest(e){this.lastRequest={event:e,timestamp:Date.now()}}updateSetupStatus(e){this.isSetupDone=e}base64Encode(e){try{return btoa(JSON.stringify(e))}catch(e){return console.warn("Failed to encode debug data",e),btoa("Failed to encode data")}}getIframeStatus(){return this.iframe?{loading:this.iframe.hasAttribute("loading"),url:this.iframe.src,readyState:this.iframe.contentDocument?.readyState?+("complete"===this.iframe.contentDocument.readyState):-1,contentWindow:!!this.iframe.contentWindow,isConnected:this.iframe.isConnected}:null}getNavigatorInfo(){return navigator?{userAgent:navigator.userAgent,language:navigator.language,onLine:navigator.onLine,screenWidth:window.screen.width,screenHeight:window.screen.height,pixelRatio:window.devicePixelRatio}:null}gatherDebugInfo(e){let t=this.getIframeStatus(),r=this.getNavigatorInfo(),o="Unknown";return e instanceof frame_connector_namespaceObject.FrakRpcError?o=`FrakRpcError: ${e.code} '${e.message}'`:e instanceof Error?o=e.message:"string"==typeof e&&(o=e),{timestamp:new Date().toISOString(),encodedUrl:btoa(window.location.href),encodedConfig:this.config?this.base64Encode(this.config):"no-config",navigatorInfo:r?this.base64Encode(r):"no-navigator",iframeStatus:t?this.base64Encode(t):"not-iframe",lastRequest:this.lastRequest?this.base64Encode(this.lastRequest):"No Frak request logged",lastResponse:this.lastResponse?this.base64Encode(this.lastResponse):"No Frak response logged",clientStatus:this.isSetupDone?"setup":"not-setup",error:o}}static empty(){return new DebugInfoGatherer}formatDebugInfo(e){let t=this.gatherDebugInfo(e);return`
2
2
  Debug Information:
3
3
  -----------------
4
4
  Timestamp: ${t.timestamp}
@@ -10,4 +10,4 @@
10
10
  Last Response: ${t.lastResponse}
11
11
  Client Status: ${t.clientStatus}
12
12
  Error: ${t.error}
13
- `.trim()}}let baseIframeProps={id:"frak-wallet",name:"frak-wallet",title:"Frak Wallet",allow:"publickey-credentials-get *; clipboard-write; web-share *",style:{width:"0",height:"0",border:"0",position:"absolute",zIndex:2000001,top:"-1000px",left:"-1000px",colorScheme:"auto"}};function createIframe({walletBaseUrl:e,config:t}){let r=document.querySelector("#frak-wallet");r&&r.remove();let a=document.createElement("iframe");return a.id=baseIframeProps.id,a.name=baseIframeProps.name,a.allow=baseIframeProps.allow,a.style.zIndex=baseIframeProps.style.zIndex.toString(),changeIframeVisibility({iframe:a,isVisible:!1}),document.body.appendChild(a),new Promise(r=>{a?.addEventListener("load",()=>r(a)),a.src=`${t?.walletUrl??e??"https://wallet.frak.id"}/listener`})}function changeIframeVisibility({iframe:e,isVisible:t}){if(!t){e.style.width="0",e.style.height="0",e.style.border="0",e.style.position="fixed",e.style.top="-1000px",e.style.left="-1000px";return}e.style.position="fixed",e.style.top="0",e.style.left="0",e.style.width="100%",e.style.height="100%",e.style.pointerEvents="auto"}function createIFrameLifecycleManager({iframe:e}){let t=new frame_connector_namespaceObject.Deferred;return{handleEvent:async r=>{if(!("iframeLifecycle"in r))return;let{iframeLifecycle:a,data:o}=r;switch(a){case"connected":t.resolve(!0);break;case"do-backup":o.backup?localStorage.setItem(BACKUP_KEY,o.backup):localStorage.removeItem(BACKUP_KEY);break;case"remove-backup":localStorage.removeItem(BACKUP_KEY);break;case"show":case"hide":changeIframeVisibility({iframe:e,isVisible:"show"===a});break;case"handshake":e.contentWindow?.postMessage({clientLifecycle:"handshake-response",data:{token:o.token,currentUrl:window.location.href}},"*");break;case"redirect":{let e=new URL(o.baseRedirectUrl);e.searchParams.has("u")?(e.searchParams.delete("u"),e.searchParams.append("u",window.location.href),window.location.href=e.toString()):window.location.href=o.baseRedirectUrl}}},isConnected:t.promise}}function createIFrameFrakClient({config:e,iframe:t}){let r,a=e?.walletUrl??"https://wallet.frak.id",o=createIFrameLifecycleManager({iframe:t}),n=new DebugInfoGatherer(e,t);if(!t.contentWindow)throw new frame_connector_namespaceObject.FrakRpcError(frame_connector_namespaceObject.RpcErrorCodes.configError,"The iframe does not have a content window");let s=(0,frame_connector_namespaceObject.createRpcClient)({emittingTransport:t.contentWindow,listeningTransport:window,targetOrigin:a,middleware:[{async onRequest(e,t){if(!await o.isConnected)throw new frame_connector_namespaceObject.FrakRpcError(frame_connector_namespaceObject.RpcErrorCodes.clientNotConnected,"The iframe provider isn't connected yet");return t}},(0,middleware_namespaceObject.createClientCompressionMiddleware)(),{onRequest:(e,t)=>(n.setLastRequest(e),t),onResponse:(e,t)=>(n.setLastResponse(e,t),t)}],lifecycleHandlers:{iframeLifecycle:async(e,t)=>{await o.handleEvent(e)}}}),c=setupHeartbeat(s,o),i=async()=>{c(),s.cleanup(),t.remove()};console.log("[Frak SDK] Initializing OpenPanel"),(r=new web_namespaceObject.OpenPanel({apiUrl:"https://op-api.gcp.frak.id",clientId:"6eacc8d7-49ac-4936-95e9-81ef29449570",trackScreenViews:!0,trackOutgoingLinks:!0,trackAttributes:!1,filter:({type:e,payload:t})=>!("track"===e&&t?.properties)||("sdkVersion"in t.properties||(t.properties={...t.properties,sdkVersion:"0.0.19"}),!0)})).setGlobalProperties({sdkVersion:"0.0.19"}),r.init();let l=postConnectionSetup({config:e,rpcClient:s,lifecycleManager:o}).then(()=>n.updateSetupStatus(!0));return{config:e,debugInfo:n,waitForConnection:o.isConnected,waitForSetup:l,request:s.request,listenerRequest:s.listen,destroy:i,openPanel:r}}function setupHeartbeat(e,t){let r,a,o=()=>e.sendLifecycle({clientLifecycle:"heartbeat"});function n(){r&&clearInterval(r),a&&clearTimeout(a)}return async function(){o(),r=setInterval(o,1e3),a=setTimeout(()=>{n(),console.log("Heartbeat timeout: connection failed")},3e4),await t.isConnected,n()}(),n}async function postConnectionSetup({config:e,rpcClient:t,lifecycleManager:r}){async function a(){let r=e.customizations?.css;r&&t.sendLifecycle({clientLifecycle:"modal-css",data:{cssLink:r}})}async function o(){let r=e.customizations?.i18n;r&&t.sendLifecycle({clientLifecycle:"modal-i18n",data:{i18n:r}})}async function n(){if("undefined"==typeof window)return;let e=window.localStorage.getItem(BACKUP_KEY);e&&t.sendLifecycle({clientLifecycle:"restore-backup",data:{backup:e}})}await r.isConnected,setupSsoUrlListener(t,r.isConnected),await Promise.allSettled([a(),o(),n()])}let locales={eur:"fr-FR",usd:"en-US",gbp:"en-GB"};function getSupportedCurrency(e){return e&&e in locales?e:"eur"}async function setupClient({config:e}){let t=prepareConfig(e),r=await createIframe({config:t});if(!r)return void console.error("Failed to create iframe");let a=createIFrameFrakClient({config:t,iframe:r});return(await a.waitForSetup,await a.waitForConnection)?a:void console.error("Failed to connect to client")}function prepareConfig(e){let t=getSupportedCurrency(e.metadata?.currency);return{...e,metadata:{...e.metadata,currency:t}}}function base64urlEncode(e){return btoa(Array.from(e,e=>String.fromCharCode(e)).join("")).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function base64urlDecode(e){let t=e.length%4;return Uint8Array.from(atob(e.replace(/-/g,"+").replace(/_/g,"/").padEnd(e.length+(0===t?0:4-t),"=")),e=>e.charCodeAt(0))}function compressJsonToB64(e){return base64urlEncode((0,frame_connector_namespaceObject.compressJson)(e))}function decompressJsonFromB64(e){return(0,frame_connector_namespaceObject.decompressJson)(base64urlDecode(e))}let external_viem_namespaceObject=require("viem"),contextKey="fCtx";function compress(e){if(e?.r)try{let t=(0,external_viem_namespaceObject.hexToBytes)(e.r);return base64urlEncode(t)}catch(t){console.error("Error compressing Frak context",{e:t,context:e})}}function decompress(e){if(e&&0!==e.length)try{let t=base64urlDecode(e);return{r:(0,external_viem_namespaceObject.bytesToHex)(t,{size:20})}}catch(t){console.error("Error decompressing Frak context",{e:t,context:e})}}function parse({url:e}){if(!e)return null;let t=new URL(e).searchParams.get(contextKey);return t?decompress(t):null}function update({url:e,context:t}){if(!e)return null;let r=parse({url:e}),a=r?{...r,...t}:t;if(!a.r)return null;let o=compress(a);if(!o)return null;let n=new URL(e);return n.searchParams.set(contextKey,o),n.toString()}function remove(e){let t=new URL(e);return t.searchParams.delete(contextKey),t.toString()}function replaceUrl({url:e,context:t}){let r;if(!window.location?.href||"undefined"==typeof window)return void console.error("No window found, can't update context");let a=e??window.location.href;(r=null!==t?update({url:a,context:t}):remove(a))&&window.history.replaceState(null,"",r.toString())}let FrakContextManager={compress,decompress,parse,update,remove,replaceUrl};function getSupportedLocale(e){return e?locales[e]??locales.eur:locales.eur}function getCurrencyAmountKey(e){return e?`${e}Amount`:"eurAmount"}function formatAmount(e,t){let r=getSupportedLocale(t),a=getSupportedCurrency(t);return e.toLocaleString(r,{style:"currency",currency:a,minimumFractionDigits:0,maximumFractionDigits:2})}function trackEvent(e,t,r={}){if(!e)return void console.debug("[Frak] No client provided, skipping event tracking");try{e.openPanel?.track(t,r)}catch(e){console.debug("[Frak] Failed to track event:",t,e)}}let productTypes={dapp:1,press:2,webshop:3,retail:4,referral:30,purchase:31},productTypesMask=Object.entries(productTypes).reduce((e,[t,r])=>(e[t]=BigInt(1)<<BigInt(r),e),{}),interactionTypes={press:{openArticle:"0xc0a24ffb",readArticle:"0xd5bd0fbe"},dapp:{proofVerifiableStorageUpdate:"0x2ab2aeef",callableVerifiableStorageUpdate:"0xa07da986"},webshop:{open:"0xb311798f"},referral:{referred:"0x010cc3b9",createLink:"0xb2c0f17c"},purchase:{started:"0xd87e90c3",completed:"0x8403aeb4",unsafeCompleted:"0x4d5b14e0"},retail:{customerMeeting:"0x74489004"}};for(var __webpack_i__ in exports.DebugInfoGatherer=__webpack_exports__.DebugInfoGatherer,exports.FrakContextManager=__webpack_exports__.FrakContextManager,exports.base64urlDecode=__webpack_exports__.base64urlDecode,exports.base64urlEncode=__webpack_exports__.base64urlEncode,exports.baseIframeProps=__webpack_exports__.baseIframeProps,exports.compressJsonToB64=__webpack_exports__.compressJsonToB64,exports.createIFrameFrakClient=__webpack_exports__.createIFrameFrakClient,exports.createIframe=__webpack_exports__.createIframe,exports.decompressJsonFromB64=__webpack_exports__.decompressJsonFromB64,exports.formatAmount=__webpack_exports__.formatAmount,exports.getCurrencyAmountKey=__webpack_exports__.getCurrencyAmountKey,exports.getSupportedCurrency=__webpack_exports__.getSupportedCurrency,exports.getSupportedLocale=__webpack_exports__.getSupportedLocale,exports.interactionTypes=__webpack_exports__.interactionTypes,exports.locales=__webpack_exports__.locales,exports.productTypes=__webpack_exports__.productTypes,exports.productTypesMask=__webpack_exports__.productTypesMask,exports.setupClient=__webpack_exports__.setupClient,exports.trackEvent=__webpack_exports__.trackEvent,__webpack_exports__)-1===["DebugInfoGatherer","FrakContextManager","base64urlDecode","base64urlEncode","baseIframeProps","compressJsonToB64","createIFrameFrakClient","createIframe","decompressJsonFromB64","formatAmount","getCurrencyAmountKey","getSupportedCurrency","getSupportedLocale","interactionTypes","locales","productTypes","productTypesMask","setupClient","trackEvent"].indexOf(__webpack_i__)&&(exports[__webpack_i__]=__webpack_exports__[__webpack_i__]);Object.defineProperty(exports,"__esModule",{value:!0});
13
+ `.trim()}}let baseIframeProps={id:"frak-wallet",name:"frak-wallet",title:"Frak Wallet",allow:"publickey-credentials-get *; clipboard-write; web-share *",style:{width:"0",height:"0",border:"0",position:"absolute",zIndex:2000001,top:"-1000px",left:"-1000px",colorScheme:"auto"}};function createIframe({walletBaseUrl:e,config:t}){let r=document.querySelector("#frak-wallet");r&&r.remove();let o=document.createElement("iframe");return o.id=baseIframeProps.id,o.name=baseIframeProps.name,o.allow=baseIframeProps.allow,o.style.zIndex=baseIframeProps.style.zIndex.toString(),changeIframeVisibility({iframe:o,isVisible:!1}),document.body.appendChild(o),new Promise(r=>{o?.addEventListener("load",()=>r(o)),o.src=`${t?.walletUrl??e??"https://wallet.frak.id"}/listener`})}function changeIframeVisibility({iframe:e,isVisible:t}){if(!t){e.style.width="0",e.style.height="0",e.style.border="0",e.style.position="fixed",e.style.top="-1000px",e.style.left="-1000px";return}e.style.position="fixed",e.style.top="0",e.style.left="0",e.style.width="100%",e.style.height="100%",e.style.pointerEvents="auto"}function findIframeInOpener(e="/listener"){if(!window.opener)return null;let t=t=>{try{return t.location.origin===window.location.origin&&t.location.pathname===e}catch{return!1}};try{let e=window.opener.frames;for(let r=0;r<e.length;r++)if(t(e[r]))return e[r];return null}catch(t){return console.error(`[findIframeInOpener] Error finding iframe with pathname ${e}:`,t),null}}function createIFrameLifecycleManager({iframe:e}){let t=new frame_connector_namespaceObject.Deferred;return{handleEvent:async r=>{if(!("iframeLifecycle"in r))return;let{iframeLifecycle:o,data:a}=r;switch(o){case"connected":t.resolve(!0);break;case"do-backup":a.backup?localStorage.setItem(BACKUP_KEY,a.backup):localStorage.removeItem(BACKUP_KEY);break;case"remove-backup":localStorage.removeItem(BACKUP_KEY);break;case"show":case"hide":changeIframeVisibility({iframe:e,isVisible:"show"===o});break;case"handshake":e.contentWindow?.postMessage({clientLifecycle:"handshake-response",data:{token:a.token,currentUrl:window.location.href}},"*");break;case"redirect":{let e=new URL(a.baseRedirectUrl);e.searchParams.has("u")?(e.searchParams.delete("u"),e.searchParams.append("u",window.location.href),window.location.href=e.toString()):window.location.href=a.baseRedirectUrl}}},isConnected:t.promise}}function createIFrameFrakClient({config:e,iframe:t}){let r,o=e?.walletUrl??"https://wallet.frak.id",a=createIFrameLifecycleManager({iframe:t}),n=new DebugInfoGatherer(e,t);if(!t.contentWindow)throw new frame_connector_namespaceObject.FrakRpcError(frame_connector_namespaceObject.RpcErrorCodes.configError,"The iframe does not have a content window");let s=(0,frame_connector_namespaceObject.createRpcClient)({emittingTransport:t.contentWindow,listeningTransport:window,targetOrigin:o,middleware:[{async onRequest(e,t){if(!await a.isConnected)throw new frame_connector_namespaceObject.FrakRpcError(frame_connector_namespaceObject.RpcErrorCodes.clientNotConnected,"The iframe provider isn't connected yet");return t}},(0,middleware_namespaceObject.createClientCompressionMiddleware)(),{onRequest:(e,t)=>(n.setLastRequest(e),t),onResponse:(e,t)=>(n.setLastResponse(e,t),t)}],lifecycleHandlers:{iframeLifecycle:async(e,t)=>{await a.handleEvent(e)}}}),c=setupHeartbeat(s,a),i=async()=>{c(),s.cleanup(),t.remove()};console.log("[Frak SDK] Initializing OpenPanel"),(r=new web_namespaceObject.OpenPanel({apiUrl:"https://op-api.gcp.frak.id",clientId:"6eacc8d7-49ac-4936-95e9-81ef29449570",trackScreenViews:!0,trackOutgoingLinks:!0,trackAttributes:!1,filter:({type:e,payload:t})=>!("track"===e&&t?.properties)||("sdkVersion"in t.properties||(t.properties={...t.properties,sdkVersion:"0.1.0"}),!0)})).setGlobalProperties({sdkVersion:"0.1.0"}),r.init();let p=postConnectionSetup({config:e,rpcClient:s,lifecycleManager:a}).then(()=>n.updateSetupStatus(!0));return{config:e,debugInfo:n,waitForConnection:a.isConnected,waitForSetup:p,request:s.request,listenerRequest:s.listen,destroy:i,openPanel:r}}function setupHeartbeat(e,t){let r,o,a=()=>e.sendLifecycle({clientLifecycle:"heartbeat"});function n(){r&&clearInterval(r),o&&clearTimeout(o)}return async function(){a(),r=setInterval(a,1e3),o=setTimeout(()=>{n(),console.log("Heartbeat timeout: connection failed")},3e4),await t.isConnected,n()}(),n}async function postConnectionSetup({config:e,rpcClient:t,lifecycleManager:r}){async function o(){let r=e.customizations?.css;r&&t.sendLifecycle({clientLifecycle:"modal-css",data:{cssLink:r}})}async function a(){let r=e.customizations?.i18n;r&&t.sendLifecycle({clientLifecycle:"modal-i18n",data:{i18n:r}})}async function n(){if("undefined"==typeof window)return;let e=window.localStorage.getItem(BACKUP_KEY);e&&t.sendLifecycle({clientLifecycle:"restore-backup",data:{backup:e}})}await r.isConnected,setupSsoUrlListener(t,r.isConnected),await Promise.allSettled([o(),a(),n()])}let locales={eur:"fr-FR",usd:"en-US",gbp:"en-GB"};function getSupportedCurrency(e){return e&&e in locales?e:"eur"}async function setupClient({config:e}){let t=prepareConfig(e),r=await createIframe({config:t});if(!r)return void console.error("Failed to create iframe");let o=createIFrameFrakClient({config:t,iframe:r});return(await o.waitForSetup,await o.waitForConnection)?o:void console.error("Failed to connect to client")}function prepareConfig(e){let t=getSupportedCurrency(e.metadata?.currency);return{...e,metadata:{...e.metadata,currency:t}}}function base64urlEncode(e){return btoa(Array.from(e,e=>String.fromCharCode(e)).join("")).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function base64urlDecode(e){let t=e.length%4;return Uint8Array.from(atob(e.replace(/-/g,"+").replace(/_/g,"/").padEnd(e.length+(0===t?0:4-t),"=")),e=>e.charCodeAt(0))}function compressJsonToB64(e){return base64urlEncode((0,frame_connector_namespaceObject.compressJson)(e))}function decompressJsonFromB64(e){return(0,frame_connector_namespaceObject.decompressJson)(base64urlDecode(e))}let external_viem_namespaceObject=require("viem"),contextKey="fCtx";function compress(e){if(e?.r)try{let t=(0,external_viem_namespaceObject.hexToBytes)(e.r);return base64urlEncode(t)}catch(t){console.error("Error compressing Frak context",{e:t,context:e})}}function decompress(e){if(e&&0!==e.length)try{let t=base64urlDecode(e);return{r:(0,external_viem_namespaceObject.bytesToHex)(t,{size:20})}}catch(t){console.error("Error decompressing Frak context",{e:t,context:e})}}function parse({url:e}){if(!e)return null;let t=new URL(e).searchParams.get(contextKey);return t?decompress(t):null}function update({url:e,context:t}){if(!e)return null;let r=parse({url:e}),o=r?{...r,...t}:t;if(!o.r)return null;let a=compress(o);if(!a)return null;let n=new URL(e);return n.searchParams.set(contextKey,a),n.toString()}function remove(e){let t=new URL(e);return t.searchParams.delete(contextKey),t.toString()}function replaceUrl({url:e,context:t}){let r;if(!window.location?.href||"undefined"==typeof window)return void console.error("No window found, can't update context");let o=e??window.location.href;(r=null!==t?update({url:o,context:t}):remove(o))&&window.history.replaceState(null,"",r.toString())}let FrakContextManager={compress,decompress,parse,update,remove,replaceUrl};function getSupportedLocale(e){return e?locales[e]??locales.eur:locales.eur}function getCurrencyAmountKey(e){return e?`${e}Amount`:"eurAmount"}function formatAmount(e,t){let r=getSupportedLocale(t),o=getSupportedCurrency(t);return e.toLocaleString(r,{style:"currency",currency:o,minimumFractionDigits:0,maximumFractionDigits:2})}function trackEvent(e,t,r={}){if(!e)return void console.debug("[Frak] No client provided, skipping event tracking");try{e.openPanel?.track(t,r)}catch(e){console.debug("[Frak] Failed to track event:",t,e)}}function sso_generateSsoUrl(e,t,r,o,a){let n=compressJsonToB64(ssoParamsToCompressed({redirectUrl:t.redirectUrl,directExit:t.directExit,lang:t.lang,productId:r,metadata:{name:o,css:a,logoUrl:t.metadata?.logoUrl,homepageLink:t.metadata?.homepageLink}})),s=new URL(e);return s.pathname="/sso",s.searchParams.set("p",n),s.toString()}function ssoParamsToCompressed(e){return{r:e.redirectUrl,d:e.directExit,l:e.lang,p:e.productId,m:{n:e.metadata?.name,css:e.metadata?.css,l:e.metadata?.logoUrl,h:e.metadata?.homepageLink}}}let productTypes={dapp:1,press:2,webshop:3,retail:4,referral:30,purchase:31},productTypesMask=Object.entries(productTypes).reduce((e,[t,r])=>(e[t]=BigInt(1)<<BigInt(r),e),{}),interactionTypes={press:{openArticle:"0xc0a24ffb",readArticle:"0xd5bd0fbe"},dapp:{proofVerifiableStorageUpdate:"0x2ab2aeef",callableVerifiableStorageUpdate:"0xa07da986"},webshop:{open:"0xb311798f"},referral:{referred:"0x010cc3b9",createLink:"0xb2c0f17c"},purchase:{started:"0xd87e90c3",completed:"0x8403aeb4",unsafeCompleted:"0x4d5b14e0"},retail:{customerMeeting:"0x74489004"}},ssoPopupFeatures="menubar=no,status=no,scrollbars=no,fullscreen=no,width=500, height=800",ssoPopupName="frak-sso";async function openSso(e,t){let{metadata:r,customizations:o,walletUrl:a}=e.config;if(t.openInSameWindow??!!t.redirectUrl)return await e.request({method:"frak_openSso",params:[t,r.name,o?.css]});let n=t.ssoPopupUrl??generateSsoUrl(a??"https://wallet.frak.id",t,computeProductId(),r.name,o?.css),s=window.open(n,ssoPopupName,ssoPopupFeatures);if(!s)throw Error("Popup was blocked. Please allow popups for this site.");return s.focus(),await e.request({method:"frak_openSso",params:[t,r.name,o?.css]})??{}}for(var __webpack_i__ in exports.DebugInfoGatherer=__webpack_exports__.DebugInfoGatherer,exports.FrakContextManager=__webpack_exports__.FrakContextManager,exports.base64urlDecode=__webpack_exports__.base64urlDecode,exports.base64urlEncode=__webpack_exports__.base64urlEncode,exports.baseIframeProps=__webpack_exports__.baseIframeProps,exports.compressJsonToB64=__webpack_exports__.compressJsonToB64,exports.createIFrameFrakClient=__webpack_exports__.createIFrameFrakClient,exports.createIframe=__webpack_exports__.createIframe,exports.decompressJsonFromB64=__webpack_exports__.decompressJsonFromB64,exports.findIframeInOpener=__webpack_exports__.findIframeInOpener,exports.formatAmount=__webpack_exports__.formatAmount,exports.generateSsoUrl=__webpack_exports__.generateSsoUrl,exports.getCurrencyAmountKey=__webpack_exports__.getCurrencyAmountKey,exports.getSupportedCurrency=__webpack_exports__.getSupportedCurrency,exports.getSupportedLocale=__webpack_exports__.getSupportedLocale,exports.interactionTypes=__webpack_exports__.interactionTypes,exports.locales=__webpack_exports__.locales,exports.productTypes=__webpack_exports__.productTypes,exports.productTypesMask=__webpack_exports__.productTypesMask,exports.setupClient=__webpack_exports__.setupClient,exports.ssoPopupFeatures=__webpack_exports__.ssoPopupFeatures,exports.ssoPopupName=__webpack_exports__.ssoPopupName,exports.trackEvent=__webpack_exports__.trackEvent,__webpack_exports__)-1===["DebugInfoGatherer","FrakContextManager","base64urlDecode","base64urlEncode","baseIframeProps","compressJsonToB64","createIFrameFrakClient","createIframe","decompressJsonFromB64","findIframeInOpener","formatAmount","generateSsoUrl","getCurrencyAmountKey","getSupportedCurrency","getSupportedLocale","interactionTypes","locales","productTypes","productTypesMask","setupClient","ssoPopupFeatures","ssoPopupName","trackEvent"].indexOf(__webpack_i__)&&(exports[__webpack_i__]=__webpack_exports__[__webpack_i__]);Object.defineProperty(exports,"__esModule",{value:!0});
package/dist/index.d.cts CHANGED
@@ -7,6 +7,11 @@ import { RpcMessage } from '@frak-labs/frame-connector';
7
7
  import { RpcResponse } from '@frak-labs/frame-connector';
8
8
  import type { SiweMessage } from 'viem/siwe';
9
9
 
10
+ export declare type AppSpecificSsoMetadata = SsoMetadata & {
11
+ name: string;
12
+ css?: string;
13
+ };
14
+
10
15
  /**
11
16
  * Decode a base64url encoded string
12
17
  * @param value The value to decode
@@ -62,6 +67,23 @@ declare function compress(context?: Partial<FrakContext>): string | undefined;
62
67
  */
63
68
  export declare type CompressedData = Uint8Array;
64
69
 
70
+ /**
71
+ * Type of compressed the sso data
72
+ */
73
+ export declare type CompressedSsoData = {
74
+ id?: Hex;
75
+ r?: string;
76
+ d?: boolean;
77
+ l?: "en" | "fr";
78
+ p: Hex;
79
+ m: {
80
+ n: string;
81
+ css?: string;
82
+ l?: string;
83
+ h?: string;
84
+ };
85
+ };
86
+
65
87
  /**
66
88
  * Compress json data
67
89
  * @param data
@@ -309,6 +331,27 @@ export declare type FinalModalStepType = GenericModalStepType<"final", {
309
331
  autoSkip?: boolean;
310
332
  }, object>;
311
333
 
334
+ /**
335
+ * Find an iframe within window.opener by pathname
336
+ *
337
+ * When a popup is opened via window.open from an iframe, window.opener points to
338
+ * the parent window, not the iframe itself. This utility searches through all frames
339
+ * in window.opener to find an iframe matching the specified pathname.
340
+ *
341
+ * @param pathname - The pathname to search for (default: "/listener")
342
+ * @returns The matching iframe window, or null if not found
343
+ *
344
+ * @example
345
+ * ```typescript
346
+ * // Find the default /listener iframe
347
+ * const listenerIframe = findIframeInOpener();
348
+ *
349
+ * // Find a custom iframe
350
+ * const customIframe = findIframeInOpener("/my-custom-iframe");
351
+ * ```
352
+ */
353
+ export declare function findIframeInOpener(pathname?: string): Window | null;
354
+
312
355
  export declare function formatAmount(amount: number, currency?: Currency): string;
313
356
 
314
357
  /**
@@ -417,6 +460,38 @@ export declare type FullInteractionTypesKey = {
417
460
  [Category in keyof typeof interactionTypes]: `${Category & string}.${keyof (typeof interactionTypes)[Category] & string}`;
418
461
  }[keyof typeof interactionTypes];
419
462
 
463
+ /**
464
+ * The full SSO params that will be used for compression
465
+ */
466
+ export declare type FullSsoParams = Omit<PrepareSsoParamsType, "metadata"> & {
467
+ metadata: AppSpecificSsoMetadata;
468
+ productId: Hex;
469
+ };
470
+
471
+ /**
472
+ * Generate SSO URL with compressed parameters
473
+ * This mirrors the wallet's getOpenSsoLink() function
474
+ *
475
+ * @param walletUrl - Base wallet URL (e.g., "https://wallet.frak.id")
476
+ * @param params - SSO parameters
477
+ * @param productId - Product identifier
478
+ * @param name - Application name
479
+ * @param css - Optional custom CSS
480
+ * @returns Complete SSO URL ready to open in popup or redirect
481
+ *
482
+ * @example
483
+ * ```ts
484
+ * const ssoUrl = generateSsoUrl(
485
+ * "https://wallet.frak.id",
486
+ * { metadata: { logoUrl: "..." }, directExit: true },
487
+ * "0x123...",
488
+ * "My App"
489
+ * );
490
+ * // Returns: https://wallet.frak.id/sso?p=<compressed_base64>
491
+ * ```
492
+ */
493
+ export declare function generateSsoUrl(walletUrl: string, params: PrepareSsoParamsType, productId: Hex, name: string, css?: string): string;
494
+
420
495
  /**
421
496
  * Represent a generic modal step type
422
497
  * @ignore
@@ -648,11 +723,27 @@ export declare type IFrameRpcSchema = [
648
723
  ReturnType: SendInteractionReturnType;
649
724
  },
650
725
  /**
651
- * Method to start a SSO
652
- * This is a one-shot request
726
+ * Method to prepare SSO (generate URL for popup)
727
+ * Returns the SSO URL that should be opened in a popup
728
+ * Only used for popup flows (not redirect flows)
653
729
  */
654
730
  {
655
- Method: "frak_sso";
731
+ Method: "frak_prepareSso";
732
+ Parameters: [
733
+ params: PrepareSsoParamsType,
734
+ name: string,
735
+ customCss?: string
736
+ ];
737
+ ReturnType: PrepareSsoReturnType;
738
+ },
739
+ /**
740
+ * Method to open/trigger SSO
741
+ * Either triggers redirect (if openInSameWindow/redirectUrl)
742
+ * Or waits for popup completion (if popup mode)
743
+ * This method handles BOTH redirect and popup flows
744
+ */
745
+ {
746
+ Method: "frak_openSso";
656
747
  Parameters: [
657
748
  params: OpenSsoParamsType,
658
749
  name: string,
@@ -965,30 +1056,16 @@ export declare type OpenInteractionSessionReturnType = {
965
1056
  * Params to start a SSO
966
1057
  * @group RPC Schema
967
1058
  */
968
- export declare type OpenSsoParamsType = {
969
- /**
970
- * Redirect URL after the SSO (optional)
971
- */
972
- redirectUrl?: string;
1059
+ export declare type OpenSsoParamsType = PrepareSsoParamsType & {
973
1060
  /**
974
- * If the SSO should directly exit after completion
975
- * @defaultValue true
976
- */
977
- directExit?: boolean;
978
- /**
979
- * If true, opens SSO in same window instead of popup
980
- * Defaults to true when redirectUrl is provided, false otherwise
1061
+ * Indicate whether we want todo the flow within the same window context, or if we want to do it with an external popup window openned
1062
+ * Note: Default true if redirectUrl is present, otherwise, false
981
1063
  */
982
1064
  openInSameWindow?: boolean;
983
1065
  /**
984
- * Language of the SSO page (optional)
985
- * It will default to the current user language (or "en" if unsupported language)
986
- */
987
- lang?: "en" | "fr";
988
- /**
989
- * Custom SSO metadata
1066
+ * Custom SSO popup url if user want additionnal customisation
990
1067
  */
991
- metadata: SsoMetadata;
1068
+ ssoPopupUrl?: string;
992
1069
  };
993
1070
 
994
1071
  /**
@@ -1020,6 +1097,43 @@ export declare type PreparedInteraction = {
1020
1097
  interactionData: Hex;
1021
1098
  };
1022
1099
 
1100
+ /**
1101
+ * Params for preparing SSO (generating URL)
1102
+ * Same as OpenSsoParamsType but without openInSameWindow (popup-only operation)
1103
+ * @group RPC Schema
1104
+ */
1105
+ export declare type PrepareSsoParamsType = {
1106
+ /**
1107
+ * Redirect URL after the SSO (optional)
1108
+ */
1109
+ redirectUrl?: string;
1110
+ /**
1111
+ * If the SSO should directly exit after completion
1112
+ * @defaultValue true
1113
+ */
1114
+ directExit?: boolean;
1115
+ /**
1116
+ * Language of the SSO page (optional)
1117
+ * It will default to the current user language (or "en" if unsupported language)
1118
+ */
1119
+ lang?: "en" | "fr";
1120
+ /**
1121
+ * Custom SSO metadata
1122
+ */
1123
+ metadata?: SsoMetadata;
1124
+ };
1125
+
1126
+ /**
1127
+ * Response after preparing SSO
1128
+ * @group RPC Schema
1129
+ */
1130
+ export declare type PrepareSsoReturnType = {
1131
+ /**
1132
+ * The SSO URL that should be opened in a popup
1133
+ */
1134
+ ssoUrl: string;
1135
+ };
1136
+
1023
1137
  /**
1024
1138
  * List of the product types per denominator
1025
1139
  */
@@ -1204,6 +1318,10 @@ export declare type SsoMetadata = {
1204
1318
  homepageLink?: string;
1205
1319
  };
1206
1320
 
1321
+ export declare const ssoPopupFeatures = "menubar=no,status=no,scrollbars=no,fullscreen=no,width=500, height=800";
1322
+
1323
+ export declare const ssoPopupName = "frak-sso";
1324
+
1207
1325
  declare type SsoRedirectCompleteEvent = {
1208
1326
  clientLifecycle: "sso-redirect-complete";
1209
1327
  data: {
package/dist/index.d.ts CHANGED
@@ -7,6 +7,11 @@ import { RpcMessage } from '@frak-labs/frame-connector';
7
7
  import { RpcResponse } from '@frak-labs/frame-connector';
8
8
  import type { SiweMessage } from 'viem/siwe';
9
9
 
10
+ export declare type AppSpecificSsoMetadata = SsoMetadata & {
11
+ name: string;
12
+ css?: string;
13
+ };
14
+
10
15
  /**
11
16
  * Decode a base64url encoded string
12
17
  * @param value The value to decode
@@ -62,6 +67,23 @@ declare function compress(context?: Partial<FrakContext>): string | undefined;
62
67
  */
63
68
  export declare type CompressedData = Uint8Array;
64
69
 
70
+ /**
71
+ * Type of compressed the sso data
72
+ */
73
+ export declare type CompressedSsoData = {
74
+ id?: Hex;
75
+ r?: string;
76
+ d?: boolean;
77
+ l?: "en" | "fr";
78
+ p: Hex;
79
+ m: {
80
+ n: string;
81
+ css?: string;
82
+ l?: string;
83
+ h?: string;
84
+ };
85
+ };
86
+
65
87
  /**
66
88
  * Compress json data
67
89
  * @param data
@@ -309,6 +331,27 @@ export declare type FinalModalStepType = GenericModalStepType<"final", {
309
331
  autoSkip?: boolean;
310
332
  }, object>;
311
333
 
334
+ /**
335
+ * Find an iframe within window.opener by pathname
336
+ *
337
+ * When a popup is opened via window.open from an iframe, window.opener points to
338
+ * the parent window, not the iframe itself. This utility searches through all frames
339
+ * in window.opener to find an iframe matching the specified pathname.
340
+ *
341
+ * @param pathname - The pathname to search for (default: "/listener")
342
+ * @returns The matching iframe window, or null if not found
343
+ *
344
+ * @example
345
+ * ```typescript
346
+ * // Find the default /listener iframe
347
+ * const listenerIframe = findIframeInOpener();
348
+ *
349
+ * // Find a custom iframe
350
+ * const customIframe = findIframeInOpener("/my-custom-iframe");
351
+ * ```
352
+ */
353
+ export declare function findIframeInOpener(pathname?: string): Window | null;
354
+
312
355
  export declare function formatAmount(amount: number, currency?: Currency): string;
313
356
 
314
357
  /**
@@ -417,6 +460,38 @@ export declare type FullInteractionTypesKey = {
417
460
  [Category in keyof typeof interactionTypes]: `${Category & string}.${keyof (typeof interactionTypes)[Category] & string}`;
418
461
  }[keyof typeof interactionTypes];
419
462
 
463
+ /**
464
+ * The full SSO params that will be used for compression
465
+ */
466
+ export declare type FullSsoParams = Omit<PrepareSsoParamsType, "metadata"> & {
467
+ metadata: AppSpecificSsoMetadata;
468
+ productId: Hex;
469
+ };
470
+
471
+ /**
472
+ * Generate SSO URL with compressed parameters
473
+ * This mirrors the wallet's getOpenSsoLink() function
474
+ *
475
+ * @param walletUrl - Base wallet URL (e.g., "https://wallet.frak.id")
476
+ * @param params - SSO parameters
477
+ * @param productId - Product identifier
478
+ * @param name - Application name
479
+ * @param css - Optional custom CSS
480
+ * @returns Complete SSO URL ready to open in popup or redirect
481
+ *
482
+ * @example
483
+ * ```ts
484
+ * const ssoUrl = generateSsoUrl(
485
+ * "https://wallet.frak.id",
486
+ * { metadata: { logoUrl: "..." }, directExit: true },
487
+ * "0x123...",
488
+ * "My App"
489
+ * );
490
+ * // Returns: https://wallet.frak.id/sso?p=<compressed_base64>
491
+ * ```
492
+ */
493
+ export declare function generateSsoUrl(walletUrl: string, params: PrepareSsoParamsType, productId: Hex, name: string, css?: string): string;
494
+
420
495
  /**
421
496
  * Represent a generic modal step type
422
497
  * @ignore
@@ -648,11 +723,27 @@ export declare type IFrameRpcSchema = [
648
723
  ReturnType: SendInteractionReturnType;
649
724
  },
650
725
  /**
651
- * Method to start a SSO
652
- * This is a one-shot request
726
+ * Method to prepare SSO (generate URL for popup)
727
+ * Returns the SSO URL that should be opened in a popup
728
+ * Only used for popup flows (not redirect flows)
653
729
  */
654
730
  {
655
- Method: "frak_sso";
731
+ Method: "frak_prepareSso";
732
+ Parameters: [
733
+ params: PrepareSsoParamsType,
734
+ name: string,
735
+ customCss?: string
736
+ ];
737
+ ReturnType: PrepareSsoReturnType;
738
+ },
739
+ /**
740
+ * Method to open/trigger SSO
741
+ * Either triggers redirect (if openInSameWindow/redirectUrl)
742
+ * Or waits for popup completion (if popup mode)
743
+ * This method handles BOTH redirect and popup flows
744
+ */
745
+ {
746
+ Method: "frak_openSso";
656
747
  Parameters: [
657
748
  params: OpenSsoParamsType,
658
749
  name: string,
@@ -965,30 +1056,16 @@ export declare type OpenInteractionSessionReturnType = {
965
1056
  * Params to start a SSO
966
1057
  * @group RPC Schema
967
1058
  */
968
- export declare type OpenSsoParamsType = {
969
- /**
970
- * Redirect URL after the SSO (optional)
971
- */
972
- redirectUrl?: string;
1059
+ export declare type OpenSsoParamsType = PrepareSsoParamsType & {
973
1060
  /**
974
- * If the SSO should directly exit after completion
975
- * @defaultValue true
976
- */
977
- directExit?: boolean;
978
- /**
979
- * If true, opens SSO in same window instead of popup
980
- * Defaults to true when redirectUrl is provided, false otherwise
1061
+ * Indicate whether we want todo the flow within the same window context, or if we want to do it with an external popup window openned
1062
+ * Note: Default true if redirectUrl is present, otherwise, false
981
1063
  */
982
1064
  openInSameWindow?: boolean;
983
1065
  /**
984
- * Language of the SSO page (optional)
985
- * It will default to the current user language (or "en" if unsupported language)
986
- */
987
- lang?: "en" | "fr";
988
- /**
989
- * Custom SSO metadata
1066
+ * Custom SSO popup url if user want additionnal customisation
990
1067
  */
991
- metadata: SsoMetadata;
1068
+ ssoPopupUrl?: string;
992
1069
  };
993
1070
 
994
1071
  /**
@@ -1020,6 +1097,43 @@ export declare type PreparedInteraction = {
1020
1097
  interactionData: Hex;
1021
1098
  };
1022
1099
 
1100
+ /**
1101
+ * Params for preparing SSO (generating URL)
1102
+ * Same as OpenSsoParamsType but without openInSameWindow (popup-only operation)
1103
+ * @group RPC Schema
1104
+ */
1105
+ export declare type PrepareSsoParamsType = {
1106
+ /**
1107
+ * Redirect URL after the SSO (optional)
1108
+ */
1109
+ redirectUrl?: string;
1110
+ /**
1111
+ * If the SSO should directly exit after completion
1112
+ * @defaultValue true
1113
+ */
1114
+ directExit?: boolean;
1115
+ /**
1116
+ * Language of the SSO page (optional)
1117
+ * It will default to the current user language (or "en" if unsupported language)
1118
+ */
1119
+ lang?: "en" | "fr";
1120
+ /**
1121
+ * Custom SSO metadata
1122
+ */
1123
+ metadata?: SsoMetadata;
1124
+ };
1125
+
1126
+ /**
1127
+ * Response after preparing SSO
1128
+ * @group RPC Schema
1129
+ */
1130
+ export declare type PrepareSsoReturnType = {
1131
+ /**
1132
+ * The SSO URL that should be opened in a popup
1133
+ */
1134
+ ssoUrl: string;
1135
+ };
1136
+
1023
1137
  /**
1024
1138
  * List of the product types per denominator
1025
1139
  */
@@ -1204,6 +1318,10 @@ export declare type SsoMetadata = {
1204
1318
  homepageLink?: string;
1205
1319
  };
1206
1320
 
1321
+ export declare const ssoPopupFeatures = "menubar=no,status=no,scrollbars=no,fullscreen=no,width=500, height=800";
1322
+
1323
+ export declare const ssoPopupName = "frak-sso";
1324
+
1207
1325
  declare type SsoRedirectCompleteEvent = {
1208
1326
  clientLifecycle: "sso-redirect-complete";
1209
1327
  data: {