@frak-labs/core-sdk 1.1.4 → 1.1.5-beta.5b65e64f
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/cdn/bundle.js +1 -1
- package/dist/actions-Cyn4asew.cjs +2 -0
- package/dist/actions-Cyn4asew.cjs.map +1 -0
- package/dist/actions-DtPVuk2e.js +2 -0
- package/dist/actions-DtPVuk2e.js.map +1 -0
- package/dist/actions.cjs +1 -1
- package/dist/actions.d.cts +3 -3
- package/dist/actions.d.ts +3 -3
- package/dist/actions.js +1 -1
- package/dist/bundle.cjs +1 -1
- package/dist/bundle.d.cts +5 -4
- package/dist/bundle.d.ts +5 -4
- package/dist/bundle.js +1 -1
- package/dist/frakContext-Bgidv-Se.js +2 -0
- package/dist/frakContext-Bgidv-Se.js.map +1 -0
- package/dist/frakContext-CeVlWFcX.cjs +2 -0
- package/dist/frakContext-CeVlWFcX.cjs.map +1 -0
- package/dist/getCurrencyAmountKey-DQcwjJiB.js +2 -0
- package/dist/getCurrencyAmountKey-DQcwjJiB.js.map +1 -0
- package/dist/getCurrencyAmountKey-h2eOiYzv.cjs +2 -0
- package/dist/getCurrencyAmountKey-h2eOiYzv.cjs.map +1 -0
- package/dist/{index-lcImYQyL.d.ts → index-BCoPwxjq.d.cts} +4 -2
- package/dist/{index-Cm1PNC9z.d.ts → index-CiHjUnfc.d.ts} +8 -6
- package/dist/{index-Cumn_FNI.d.cts → index-D25z5B9K.d.cts} +8 -6
- package/dist/{index-DR4n-QOe.d.cts → index-trhAN7q9.d.ts} +4 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1 -1
- package/dist/merchantInformation--tLnowAN.d.ts +344 -0
- package/dist/merchantInformation-BSuahpjl.d.cts +344 -0
- package/dist/{openSso-BErs7_Bg.d.cts → openSso-CENLMWJx.d.ts} +4 -279
- package/dist/{openSso-DRLH4D9a.d.ts → openSso-CKvkAGts.d.cts} +4 -279
- package/dist/rewards.cjs +2 -0
- package/dist/rewards.cjs.map +1 -0
- package/dist/rewards.d.cts +101 -0
- package/dist/rewards.d.ts +101 -0
- package/dist/rewards.js +2 -0
- package/dist/rewards.js.map +1 -0
- package/dist/src-BXtnEE3a.js +2 -0
- package/dist/src-BXtnEE3a.js.map +1 -0
- package/dist/src-TpX-7MwJ.cjs +2 -0
- package/dist/src-TpX-7MwJ.cjs.map +1 -0
- package/package.json +16 -3
- package/src/actions/getMerchantInformation.test.ts +2 -0
- package/src/actions/referral/processReferral.test.ts +4 -4
- package/src/actions/referral/processReferral.ts +4 -4
- package/src/clients/createIFrameFrakClient.ts +7 -7
- package/src/clients/transports/iframeLifecycleManager.ts +5 -21
- package/src/config/sdkConfigStore.ts +6 -3
- package/src/index.ts +6 -0
- package/src/rewards/conditions.test.ts +60 -0
- package/src/rewards/conditions.ts +72 -0
- package/src/rewards/example.test.ts +92 -0
- package/src/rewards/example.ts +57 -0
- package/src/rewards/format.test.ts +143 -0
- package/src/rewards/format.ts +81 -0
- package/src/rewards/index.ts +27 -0
- package/src/rewards/select.test.ts +212 -0
- package/src/rewards/select.ts +132 -0
- package/src/rewards/value.test.ts +89 -0
- package/src/rewards/value.ts +73 -0
- package/src/types/index.ts +6 -0
- package/src/types/rpc/merchantInformation.ts +120 -13
- package/src/utils/analytics/events/lifecycle.ts +1 -1
- package/src/utils/analytics/events/referral.ts +3 -3
- package/src/utils/browser/deepLinkWithFallback.ts +4 -1
- package/src/utils/browser/inAppBrowser.ts +2 -2
- package/src/utils/cache/withCache.ts +30 -11
- package/src/utils/iframe/iframeHelper.test.ts +39 -0
- package/src/utils/iframe/iframeHelper.ts +31 -2
- package/dist/actions-D5GbNcvt.cjs +0 -1
- package/dist/actions-DE2dHqtp.js +0 -1
- package/dist/frakContext-Bl17GGa3.cjs +0 -1
- package/dist/frakContext-DmI5CaSw.js +0 -1
- package/dist/src-5vpUuhfY.js +0 -1
- package/dist/src-BQsjMpvA.cjs +0 -1
package/dist/bundle.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { A as
|
|
3
|
-
import { C as
|
|
4
|
-
|
|
1
|
+
import { S as UtmParams, _ as LocalizedI18nConfig, a as MerchantReward, b as TrackArrivalParams, c as RuleConditions, d as InteractionTypeKey, f as Currency, g as ListenerPreloadOption, h as Language, i as GetMerchantInformationReturnType, l as RuleField, m as I18nConfig, n as ConditionOperator, o as RewardTier, p as FrakWalletSdkConfig, r as EstimatedReward, s as RuleCondition, t as ConditionGroup, u as TokenAmountType, v as AttributionDefaults, x as TrackArrivalResult, y as AttributionParams } from "./merchantInformation--tLnowAN.js";
|
|
2
|
+
import { A as SendTransactionTxType, B as FinalActionType, C as DisplayModalParamsType, D as ModalStepTypes, E as ModalRpcStepsResultType, F as OpenSsoParamsType, G as MerchantConfigResponse, H as ModalStepMetadata, I as OpenSsoReturnType, J as SdkResolvedConfig, K as ResolvedPlacement, L as PrepareSsoParamsType, M as SiweAuthenticateReturnType, N as SiweAuthenticationParams, O as SendTransactionModalStepType, P as LoginModalStepType, R as PrepareSsoReturnType, S as SharingPageProduct, T as ModalRpcStepsInput, U as IFrameLifecycleEvent, V as FinalModalStepType, W as ClientLifecycleEvent, _ as EmbeddedViewActionReferred, a as FrakContextV1, b as DisplaySharingPageParamsType, c as FrakLifecycleEvent, d as WalletStatusReturnType, f as UserReferralStatusType, g as LoggedOutEmbeddedView, h as DisplayEmbeddedWalletResultType, i as FrakContext, j as SiweAuthenticateModalStepType, k as SendTransactionReturnType, l as IFrameTransport, m as DisplayEmbeddedWalletParamsType, n as ssoPopupFeatures, o as FrakContextV2, p as SendInteractionParamsType, q as ResolvedSdkConfig, r as ssoPopupName, s as FrakClient, t as openSso, u as IFrameRpcSchema, v as EmbeddedViewActionSharing, w as ModalRpcMetadata, x as DisplaySharingPageResultType, y as LoggedInEmbeddedView, z as SsoMetadata } from "./openSso-CENLMWJx.js";
|
|
3
|
+
import { A as DEEP_LINK_SCHEME, C as triggerDeepLinkWithFallback, D as MergeAttributionInput, E as SdkHandshakeFailureReason, F as createIFrameFrakClient, I as CompressedData, L as HashProtectedData, M as getClientId, N as getBackendUrl, O as mergeAttribution, P as setupClient, R as KeyProvider, S as DeepLinkFallbackOptions, T as SdkEventMap, _ as withCache, a as FullSsoParams, b as isMobile, c as findIframeInOpener, d as formatAmount, f as decompressJsonFromB64, g as clearAllCache, h as base64urlEncode, i as CompressedSsoData, j as sdkConfigStore, k as FrakContextManager, l as getSupportedCurrency, m as base64urlDecode, n as getQueryParamCaseInsensitive, o as generateSsoUrl, p as compressJsonToB64, r as AppSpecificSsoMetadata, s as baseIframeProps, t as deleteQueryParamCaseInsensitive, u as getCurrencyAmountKey, v as isIOS, w as trackEvent, x as redirectToExternalBrowser, y as isInAppBrowser } from "./index-CiHjUnfc.js";
|
|
4
|
+
import { C as displayEmbeddedWallet, S as displayModal, _ as getUserReferralStatus, a as ModalBuilder, b as ensureIdentity, c as watchWalletStatus, d as REFERRAL_SUCCESS_EVENT, f as setupReferral, g as prepareSso, h as processReferral, i as sendTransaction, l as trackPurchaseStatus, m as ProcessReferralOptions, n as siweAuthenticate, o as ModalStepBuilder, p as referralInteraction, r as SendTransactionParams, s as modalBuilder, t as SiweAuthenticateModalParams, u as sendInteraction, v as getMergeToken, x as displaySharingPage, y as getMerchantInformation } from "./index-trhAN7q9.js";
|
|
5
|
+
export { type AppSpecificSsoMetadata, type AttributionDefaults, type AttributionParams, type ClientLifecycleEvent, type CompressedData, type CompressedSsoData, type ConditionGroup, type ConditionOperator, type Currency, DEEP_LINK_SCHEME, type DeepLinkFallbackOptions, type DisplayEmbeddedWalletParamsType, type DisplayEmbeddedWalletResultType, type DisplayModalParamsType, type DisplaySharingPageParamsType, type DisplaySharingPageResultType, type EmbeddedViewActionReferred, type EmbeddedViewActionSharing, type EstimatedReward, type FinalActionType, type FinalModalStepType, type FrakClient, type FrakContext, FrakContextManager, type FrakContextV1, type FrakContextV2, type FrakLifecycleEvent, type FrakWalletSdkConfig, type FullSsoParams, type GetMerchantInformationReturnType, type HashProtectedData, type I18nConfig, type IFrameLifecycleEvent, type IFrameRpcSchema, type IFrameTransport, type InteractionTypeKey, type KeyProvider, type Language, type ListenerPreloadOption, type LocalizedI18nConfig, type LoggedInEmbeddedView, type LoggedOutEmbeddedView, type LoginModalStepType, type MerchantConfigResponse, type MerchantReward, type MergeAttributionInput, type ModalBuilder, type ModalRpcMetadata, type ModalRpcStepsInput, type ModalRpcStepsResultType, type ModalStepBuilder, type ModalStepMetadata, type ModalStepTypes, type OpenSsoParamsType, type OpenSsoReturnType, type PrepareSsoParamsType, type PrepareSsoReturnType, type ProcessReferralOptions, REFERRAL_SUCCESS_EVENT, type ResolvedPlacement, type ResolvedSdkConfig, type RewardTier, type RuleCondition, type RuleConditions, type RuleField, type SdkEventMap, type SdkHandshakeFailureReason, type SdkResolvedConfig, type SendInteractionParamsType, type SendTransactionModalStepType, type SendTransactionParams, type SendTransactionReturnType, type SendTransactionTxType, type SharingPageProduct, type SiweAuthenticateModalParams, type SiweAuthenticateModalStepType, type SiweAuthenticateReturnType, type SiweAuthenticationParams, type SsoMetadata, type TokenAmountType, type TrackArrivalParams, type TrackArrivalResult, type UserReferralStatusType, type UtmParams, type WalletStatusReturnType, base64urlDecode, base64urlEncode, baseIframeProps, clearAllCache, compressJsonToB64, createIFrameFrakClient, decompressJsonFromB64, deleteQueryParamCaseInsensitive, displayEmbeddedWallet, displayModal, displaySharingPage, ensureIdentity, findIframeInOpener, formatAmount, generateSsoUrl, getBackendUrl, getClientId, getCurrencyAmountKey, getMerchantInformation, getMergeToken, getQueryParamCaseInsensitive, getSupportedCurrency, getUserReferralStatus, isIOS, isInAppBrowser, isMobile, mergeAttribution, modalBuilder, openSso, prepareSso, processReferral, redirectToExternalBrowser, referralInteraction, sdkConfigStore, sendInteraction, sendTransaction, setupClient, setupReferral, siweAuthenticate, ssoPopupFeatures, ssoPopupName, trackEvent, trackPurchaseStatus, triggerDeepLinkWithFallback, watchWalletStatus, withCache };
|
package/dist/bundle.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{_ as e,a as t,c as n,d as r,f as i,g as a,h as o,l as s,m as c,o as l,p as u,s as d,t as f,u as p,v as m,y as h}from"./frakContext-
|
|
1
|
+
import{_ as e,a as t,c as n,d as r,f as i,g as a,h as o,l as s,m as c,o as l,p as u,s as d,t as f,u as p,v as m,y as h}from"./frakContext-Bgidv-Se.js";import{a as g,c as _,d as v,f as y,i as b,l as x,n as S,o as C,r as w,s as T,t as E,u as D}from"./src-BXtnEE3a.js";import{n as O,r as k,t as A}from"./getCurrencyAmountKey-DQcwjJiB.js";import{_ as j,a as M,c as N,d as P,f as F,g as I,h as L,i as R,l as z,m as B,n as V,o as H,p as U,r as W,s as G,t as K,u as q,v as J}from"./actions-DtPVuk2e.js";export{y as DEEP_LINK_SCHEME,f as FrakContextManager,H as REFERRAL_SUCCESS_EVENT,u as base64urlDecode,c as base64urlEncode,g as baseIframeProps,e as clearAllCache,i as compressJsonToB64,b as createIFrameFrakClient,w as decompressJsonFromB64,t as deleteQueryParamCaseInsensitive,J as displayEmbeddedWallet,j as displayModal,I as displaySharingPage,L as ensureIdentity,C as findIframeInOpener,O as formatAmount,r as generateSsoUrl,a as getBackendUrl,h as getClientId,A as getCurrencyAmountKey,B as getMerchantInformation,U as getMergeToken,l as getQueryParamCaseInsensitive,k as getSupportedCurrency,F as getUserReferralStatus,T as isIOS,_ as isInAppBrowser,x as isMobile,E as mergeAttribution,W as modalBuilder,n as openSso,P as prepareSso,z as processReferral,D as redirectToExternalBrowser,N as referralInteraction,o as sdkConfigStore,q as sendInteraction,V as sendTransaction,S as setupClient,G as setupReferral,K as siweAuthenticate,s as ssoPopupFeatures,p as ssoPopupName,d as trackEvent,M as trackPurchaseStatus,v as triggerDeepLinkWithFallback,R as watchWalletStatus,m as withCache};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{jsonEncode as e}from"@frak-labs/frame-connector";const t=`frak-client-id`;function n(){return typeof crypto<`u`&&crypto.randomUUID?crypto.randomUUID():`xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e===`x`?t:t&3|8).toString(16)})}function r(){if(typeof window>`u`||!window.localStorage)return console.warn(`[Frak SDK] No Window / localStorage available to save the clientId`),n();let e=localStorage.getItem(t);return e||(e=n(),localStorage.setItem(t,e)),e}var i=class extends Map{maxSize;constructor(e){super(),this.maxSize=e}get(e){let t=super.get(e);return super.has(e)&&(super.delete(e),super.set(e,t)),t}set(e,t){if(super.has(e)&&super.delete(e),super.set(e,t),this.maxSize&&this.size>this.maxSize){let e=super.keys().next().value;e!==void 0&&super.delete(e)}return this}};let a;function o(){return a||={promiseCache:new i(1024),responseCache:new i(1024),failureCache:new i(1024)},a}async function s(e,{cacheKey:t,cacheTime:n=3e4}){let{promiseCache:r,responseCache:i,failureCache:a}=o();if(n>0){let e=i.get(t);if(e&&Date.now()-e.created<n)return e.data}let s=a.get(t);if(s&&Date.now()-s<1e3)throw Error(`Cache: ${t} recently failed, backing off`);let c=r.get(t);c||(c=e(),r.set(t,c));try{let e=await c;return i.set(t,{data:e,created:Date.now()}),a.delete(t),e}catch(e){throw a.set(t,Date.now()),e}finally{r.delete(t)}}function c(){a&&(a.promiseCache.clear(),a.responseCache.clear(),a.failureCache.clear())}const l=`https://backend.frak.id`;function u(e){return e.includes(`localhost:3000`)||e.includes(`localhost:3010`)}function d(e){return u(e)?`https://localhost:3030`:e.includes(`wallet-dev.frak.id`)||e.includes(`wallet.gcp-dev.frak.id`)?`https://backend.gcp-dev.frak.id`:l}function f(e){if(e)return d(e);if(typeof window<`u`){let e=window.FrakSetup?.client?.config?.walletUrl;if(e)return d(e)}return l}const p=`__frakSdkConfig`,m=`frak-config-cache`,h=`frak-merchant-id`,g={key:m},_=typeof window<`u`;function v(){return{isResolved:!1,merchantId:``}}let y=null;function b(){if(!_)return null;try{let e=localStorage.getItem(g.key);if(!e)return null;let t=JSON.parse(e);return t.config?.isResolved?(y=t,t):null}catch{return null}}function x(){return(y??b())?.config}function ee(){let e=y??b();return e?Date.now()-e.timestamp<3e4:!1}function S(e){if(!(!_||!e.isResolved))try{let t={config:e,timestamp:Date.now()};localStorage.setItem(g.key,JSON.stringify(t)),y=t}catch{}}function C(){if(_){y=null;try{localStorage.removeItem(g.key)}catch{}}}function w(){_&&(window[p]||(window[p]=x()??v()))}function T(){return _?(w(),window[p]??v()):v()}function E(e){_&&window.dispatchEvent(new CustomEvent(`frak:config`,{detail:e}))}function te(e){return e??(_?window.location.hostname:``)}async function ne(e,t,n){try{let r=f(t),i=n?`&lang=${encodeURIComponent(n)}`:``,a=await fetch(`${r}/user/merchant/resolve?domain=${encodeURIComponent(e)}${i}`);if(!a.ok){console.warn(`[Frak SDK] Merchant lookup failed for domain ${e}: ${a.status}`);return}let o=await a.json();if(_)try{sessionStorage.setItem(h,o.merchantId)}catch{}return o}catch(e){console.warn(`[Frak SDK] Failed to fetch merchant config:`,e);return}}const D={getConfig:T,get isResolved(){return T().isResolved},get isCacheFresh(){return ee()},setCacheScope(e,t){g.key=`${m}:${`${e}:${t??``}`}`,y=null},setConfig(e){if(_&&(window[p]=e),S(e),E(e),_&&e.merchantId)try{sessionStorage.setItem(h,e.merchantId)}catch{}},reset(){let e=x()??v();_&&(window[p]=e),E(e)},clearCache(){if(C(),c(),_)try{sessionStorage.removeItem(h)}catch{}},resolve(e,t,n){let r=te(e);return r?s(async()=>{let e=await ne(r,t,n);if(!e)throw Error(`Config resolution returned empty`);return e},{cacheKey:`sdkConfig:${r}:${n??``}`,cacheTime:1/0}).catch(()=>void 0):Promise.resolve(void 0)},getMerchantId(){let e=T();if(e.isResolved&&e.merchantId)return e.merchantId;if(_)try{return sessionStorage.getItem(h)??void 0}catch{}},async resolveMerchantId(e,t){return D.getMerchantId()||(await D.resolve(e,t))?.merchantId}};function O(e){return btoa(Array.from(e,e=>String.fromCharCode(e)).join(``)).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``)}function k(e){let t=e.length%4;return Uint8Array.from(atob(e.replace(/-/g,`+`).replace(/_/g,`/`).padEnd(e.length+(t===0?0:4-t),`=`)),e=>e.charCodeAt(0))}function A(t){return O(e(t))}function j(e,t,n,r,i,a){let o=A(re({redirectUrl:t.redirectUrl,directExit:t.directExit,lang:t.lang,merchantId:n,metadata:{name:r,css:a,logoUrl:t.metadata?.logoUrl,homepageLink:t.metadata?.homepageLink},clientId:i})),s=new URL(e);return s.pathname=`/sso`,s.searchParams.set(`p`,o),s.toString()}function re(e){return{r:e.redirectUrl,cId:e.clientId,d:e.directExit,l:e.lang,m:e.merchantId,md:{n:e.metadata?.name,css:e.metadata?.css,l:e.metadata?.logoUrl,h:e.metadata?.homepageLink}}}const M=`menubar=no,status=no,scrollbars=no,fullscreen=no,width=500, height=800`,N=`frak-sso`;async function P(e,t){let{metadata:n,customizations:i,walletUrl:a}=e.config,o={...t,directExit:t.directExit??!t.redirectUrl};if(o.openInSameWindow??!!o.redirectUrl)return await e.request({method:`frak_openSso`,params:[o,n.name,i?.css]});let s=o.ssoPopupUrl??j(a??`https://wallet.frak.id`,o,await D.resolveMerchantId()??``,n.name,r(),i?.css),c=window.open(s,N,M);if(!c)throw Error(`Popup was blocked. Please allow popups for this site.`);return c.focus(),await e.request({method:`frak_openSso`,params:[o,n.name,i?.css]})??{}}function F(e,t,n){if(!e){console.debug(`[Frak] No client provided, skipping event tracking`);return}try{e.openPanel?.track(t,n)}catch(e){console.debug(`[Frak] Failed to track event:`,t,e)}}function I(e,t){let n=e.get(t);if(n!==null)return n;let r=t.toLowerCase();for(let[t,n]of e)if(t.toLowerCase()===r)return n;return null}function L(e,t){let n=t.toLowerCase(),r=[...e.keys()].filter(e=>e.toLowerCase()===n);for(let t of r)e.delete(t)}const R=/^0x[a-fA-F0-9]{40}$/;function z(e){return typeof e==`string`&&R.test(e)}function ie(e,t){return e.toLowerCase()===t.toLowerCase()}function B(e){let t=new Uint8Array(20);for(let n=0;n<20;n++){let r=Number.parseInt(e.substring(2+n*2,4+n*2),16);if(Number.isNaN(r))throw Error(`Invalid address: ${e}`);t[n]=r}return t}const V=Array.from({length:256},(e,t)=>t.toString(16).padStart(2,`0`));function H(e){let t=`0x`;for(let n=0;n<20;n++)t+=V[e[n]];return t}function U(e){return`r`in e&&!(`v`in e)}function W(e){return`v`in e&&e.v===2}const G=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function K(e){return typeof e==`string`&&G.test(e)}function q(e){let t=e.replace(/-/g,``),n=new Uint8Array(16);for(let e=0;e<16;e++)n[e]=Number.parseInt(t.substring(e*2,e*2+2),16);return n}function J(e){let t=``;for(let n=0;n<16;n++)t+=e[n].toString(16).padStart(2,`0`);return`${t.slice(0,8)}-${t.slice(8,12)}-${t.slice(12,16)}-${t.slice(16,20)}-${t.slice(20,32)}`}function ae(e){if(!K(e.m)||!Number.isInteger(e.t)||e.t<0||e.t>4294967295)return null;let t=typeof e.c==`string`&&e.c.length>0,n=typeof e.w==`string`&&z(e.w);if(!t&&!n||t&&!K(e.c))return null;let r=new Uint8Array(21+(t?16:0)+(n?20:0)),i=new DataView(r.buffer,r.byteOffset,r.byteLength),a=0;return r[a++]=2|(t?16:0)|(n?32:0),r.set(q(e.m),a),a+=16,i.setUint32(a,e.t,!1),a+=4,t&&(r.set(q(e.c),a),a+=16),n&&(r.set(B(e.w),a),a+=20),r}function oe(e){if(e.length<21)return null;let t=e[0];if((t&15)!=2||t&192)return null;let n=(t&16)!=0,r=(t&32)!=0;if(!n&&!r)return null;let i=21+(n?16:0)+(r?20:0);if(e.length!==i)return null;let a=1,o=J(e.subarray(a,a+16));a+=16;let s=new DataView(e.buffer,e.byteOffset,e.byteLength).getUint32(a,!1);a+=4;let c={v:2,m:o,t:s};if(n&&(c.c=J(e.subarray(a,a+16)),a+=16),r){let t=H(e.subarray(a,a+20));if(!z(t))return null;c.w=t,a+=20}return c}const Y=`fCtx`;function X(e){if(e)try{if(W(e)){let t=ae(e);return t?O(t):void 0}return O(B(e.r))}catch(t){console.error(`Error compressing Frak context`,{e:t,context:e})}}function Z(e){if(!(!e||e.length===0))try{let t=k(e);if(t.length!==20)return oe(t)||void 0;let n=H(t);if(z(n))return{r:n}}catch(t){console.error(`Error decompressing Frak context`,{e:t,context:e})}}function se({url:e}){if(!e)return null;let t=I(new URL(e).searchParams,Y);return t?Z(t):null}function ce(e){return{utm_source:e.utmSource??`frak`,utm_medium:e.utmMedium??void 0,utm_campaign:e.utmCampaign??void 0,utm_content:e.utmContent,utm_term:e.utmTerm,via:e.via??void 0,ref:e.ref??void 0}}function le(e,t){let n=ce(t??{});for(let[t,r]of Object.entries(n))r===void 0||r===``||e.searchParams.has(t)||e.searchParams.set(t,r)}function Q({url:e,context:t,attribution:n}){if(!e)return null;let r=X(t);if(!r)return null;let i=new URL(e);return L(i.searchParams,Y),i.searchParams.set(Y,r),le(i,n),i.toString()}function $(e){let t=new URL(e);return L(t.searchParams,Y),t.toString()}function ue({url:e,context:t}){if(!window.location?.href||typeof window>`u`){console.error(`No window found, can't update context`);return}let n=e??window.location.href,r;r=t===null?$(n):Q({url:n,context:t}),r&&window.history.replaceState(null,``,r.toString())}const de={compress:X,decompress:Z,parse:se,update:Q,remove:$,replaceUrl:ue};export{c as _,L as a,P as c,j as d,A as f,f as g,D as h,ie as i,M as l,O as m,U as n,I as o,k as p,W as r,F as s,de as t,N as u,s as v,r as y};
|
|
2
|
+
//# sourceMappingURL=frakContext-Bgidv-Se.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frakContext-Bgidv-Se.js","names":[],"sources":["../src/config/clientId.ts","../src/utils/cache/lruMap.ts","../src/utils/cache/withCache.ts","../src/config/backendUrl.ts","../src/config/sdkConfigStore.ts","../src/utils/compression/b64.ts","../src/utils/compression/compress.ts","../src/utils/sso/sso.ts","../src/actions/openSso.ts","../src/utils/analytics/trackEvent.ts","../src/utils/url/queryParams.ts","../src/context/address.ts","../src/types/context.ts","../src/context/frakContextV2Codec.ts","../src/context/frakContext.ts"],"sourcesContent":["/**\n * Client ID utilities for anonymous tracking\n * Generates and persists a UUID fingerprint for referral attribution\n */\n\nconst CLIENT_ID_KEY = \"frak-client-id\";\n\n/**\n * Generate a UUID v4\n * Uses crypto.randomUUID if available, otherwise falls back to a manual implementation\n */\nfunction generateUUID(): string {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older browsers\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\n/**\n * Get the client ID from localStorage, creating one if it doesn't exist\n * @returns The client ID (UUID format)\n */\nexport function getClientId(): string {\n if (typeof window === \"undefined\" || !window.localStorage) {\n // SSR or no localStorage - generate ephemeral ID\n console.warn(\n \"[Frak SDK] No Window / localStorage available to save the clientId\"\n );\n return generateUUID();\n }\n\n let clientId = localStorage.getItem(CLIENT_ID_KEY);\n if (!clientId) {\n clientId = generateUUID();\n localStorage.setItem(CLIENT_ID_KEY, clientId);\n }\n return clientId;\n}\n","/**\n * Map with a LRU (Least Recently Used) eviction policy.\n *\n * When the map exceeds `maxSize`, the least recently accessed entry is removed.\n * Accessing a key via `get()` promotes it to \"most recently used\".\n *\n * Adapted from viem's LruMap utility.\n * @link https://en.wikipedia.org/wiki/Cache_replacement_policies#LRU\n */\nexport class LruMap<TValue = unknown> extends Map<string, TValue> {\n maxSize: number;\n\n constructor(size: number) {\n super();\n this.maxSize = size;\n }\n\n override get(key: string) {\n const value = super.get(key);\n if (super.has(key)) {\n // Move to end (most recently used)\n super.delete(key);\n super.set(key, value as TValue);\n }\n return value;\n }\n\n override set(key: string, value: TValue) {\n if (super.has(key)) super.delete(key);\n super.set(key, value);\n // Evict least recently used if over capacity\n if (this.maxSize && this.size > this.maxSize) {\n const firstKey = super.keys().next().value;\n if (firstKey !== undefined) super.delete(firstKey);\n }\n return this;\n }\n}\n","import { LruMap } from \"./lruMap\";\n\ntype CacheEntry<TData> = {\n data: TData;\n created: number;\n};\n\n/** Default cache time: 30 seconds */\nexport const DEFAULT_CACHE_TIME = 30_000;\n\n/** Short negative cache to avoid flooding on transient failures */\nconst NEGATIVE_CACHE_TIME = 1_000;\n\ntype CacheStore = {\n /** In-flight promises (dedup concurrent calls) */\n promiseCache: LruMap<Promise<unknown>>;\n /** Resolved responses (TTL-based) */\n responseCache: LruMap<CacheEntry<unknown>>;\n /** Recently failed keys to avoid request floods */\n failureCache: LruMap<number>;\n};\n\n// Allocate the three LruMaps lazily on first use rather than at module init.\n// Eager top-level allocation is a side effect at odds with `sideEffects: false`\n// and runs for every consumer that merely imports this module.\nlet store: CacheStore | undefined;\nfunction caches(): CacheStore {\n if (!store) {\n store = {\n promiseCache: new LruMap<Promise<unknown>>(1024),\n responseCache: new LruMap<CacheEntry<unknown>>(1024),\n failureCache: new LruMap<number>(1024),\n };\n }\n return store;\n}\n\ntype WithCacheOptions = {\n /** The key to cache the data against */\n cacheKey: string;\n /** Time in ms that cached data will remain valid. Default: 30_000 (30s). Set to 0 to disable caching. */\n cacheTime?: number;\n};\n\n/**\n * Returns the result of a given promise, and caches the result for\n * subsequent invocations against a provided cache key.\n *\n * Also deduplicates concurrent calls — if multiple callers request the same\n * cache key while the promise is pending, they share the same promise.\n *\n * @example\n * ```ts\n * // First call fetches, subsequent calls return cached data for 30s\n * const data = await withCache(\n * () => client.request({ method: \"frak_getMerchantInformation\" }),\n * { cacheKey: \"merchantInfo\", cacheTime: 30_000 }\n * );\n * ```\n */\nexport async function withCache<TData>(\n fn: () => Promise<TData>,\n { cacheKey, cacheTime = DEFAULT_CACHE_TIME }: WithCacheOptions\n): Promise<TData> {\n const { promiseCache, responseCache, failureCache } = caches();\n // Check response cache — return immediately if fresh\n if (cacheTime > 0) {\n const cached = responseCache.get(cacheKey) as\n | CacheEntry<TData>\n | undefined;\n if (cached) {\n const age = Date.now() - cached.created;\n if (age < cacheTime) return cached.data;\n }\n }\n\n // Check if this key recently failed — back off briefly\n const lastFailure = failureCache.get(cacheKey);\n if (lastFailure && Date.now() - lastFailure < NEGATIVE_CACHE_TIME) {\n throw new Error(`Cache: ${cacheKey} recently failed, backing off`);\n }\n\n // Check if there's already a pending promise (dedup concurrent calls)\n let promise = promiseCache.get(cacheKey) as Promise<TData> | undefined;\n if (!promise) {\n promise = fn();\n promiseCache.set(cacheKey, promise);\n }\n\n try {\n const data = await promise;\n // Store the response with a timestamp\n responseCache.set(cacheKey, { data, created: Date.now() });\n // Clear any previous failure\n failureCache.delete(cacheKey);\n return data;\n } catch (error) {\n // Record the failure timestamp\n failureCache.set(cacheKey, Date.now());\n throw error;\n } finally {\n // Clear the promise cache so subsequent calls can re-fetch after TTL\n promiseCache.delete(cacheKey);\n }\n}\n\n/**\n * Get a cache handle for a specific key, useful for manual invalidation.\n *\n * @example\n * ```ts\n * // Invalidate merchant info cache after a mutation\n * getCache(\"frak_getMerchantInformation\").clear();\n * ```\n */\nexport function getCache(cacheKey: string) {\n return {\n /** Clear both the pending promise and the cached response */\n clear: () => {\n const { promiseCache, responseCache } = caches();\n promiseCache.delete(cacheKey);\n responseCache.delete(cacheKey);\n },\n /** Check if a non-expired response exists */\n has: (cacheTime: number = DEFAULT_CACHE_TIME) => {\n const { responseCache } = caches();\n const cached = responseCache.get(cacheKey);\n if (!cached) return false;\n return Date.now() - cached.created < cacheTime;\n },\n };\n}\n\n/**\n * Clear all cached data (both pending promises and resolved responses).\n * Called automatically when the client is destroyed.\n */\nexport function clearAllCache() {\n if (!store) return;\n store.promiseCache.clear();\n store.responseCache.clear();\n store.failureCache.clear();\n}\n","/**\n * Default production backend URL\n */\nconst DEFAULT_BACKEND_URL = \"https://backend.frak.id\";\n\n/**\n * Check if wallet URL is local development (port 3000 or 3010)\n */\nfunction isLocalDevelopment(walletUrl: string): boolean {\n return (\n walletUrl.includes(\"localhost:3000\") ||\n walletUrl.includes(\"localhost:3010\")\n );\n}\n\n/**\n * Derive backend URL from wallet URL\n * Maps wallet URLs to their corresponding backend URLs\n */\nfunction deriveBackendUrl(walletUrl: string): string {\n if (isLocalDevelopment(walletUrl)) {\n return \"https://localhost:3030\";\n }\n // Dev environment\n if (\n walletUrl.includes(\"wallet-dev.frak.id\") ||\n walletUrl.includes(\"wallet.gcp-dev.frak.id\")\n ) {\n return \"https://backend.gcp-dev.frak.id\";\n }\n // Production\n return DEFAULT_BACKEND_URL;\n}\n\n/**\n * Get the backend URL for API calls\n * Tries to derive from SDK config, falls back to production\n *\n * @param walletUrl - Optional wallet URL to derive from (overrides global config)\n */\nexport function getBackendUrl(walletUrl?: string): string {\n // If explicit walletUrl provided, derive from it\n if (walletUrl) {\n return deriveBackendUrl(walletUrl);\n }\n\n // Try to get from global FrakSetup config\n if (typeof window !== \"undefined\") {\n const configWalletUrl = (\n window as {\n FrakSetup?: { client?: { config?: { walletUrl?: string } } };\n }\n ).FrakSetup?.client?.config?.walletUrl;\n\n if (configWalletUrl) {\n return deriveBackendUrl(configWalletUrl);\n }\n }\n\n // Fallback to production\n return DEFAULT_BACKEND_URL;\n}\n","/**\n * SDK config store — reactive singleton for the resolved merchant config.\n *\n * State lives directly on `window.__frakSdkConfig`.\n * Reactivity is handled via the `frak:config` CustomEvent on `window`.\n * Resolved configs are cached in localStorage (30 s TTL, stale-while-revalidate).\n *\n * Backend fetch responses are cached and deduplicated via `withCache`.\n * Also owns the `frak-merchant-id` sessionStorage compatibility key.\n */\n\nimport type { Language } from \"../types/config\";\nimport type {\n MerchantConfigResponse,\n SdkResolvedConfig,\n} from \"../types/resolvedConfig\";\nimport { clearAllCache, withCache } from \"../utils/cache\";\nimport { getBackendUrl } from \"./backendUrl\";\n\nconst GLOBAL_KEY = \"__frakSdkConfig\";\nconst CACHE_TTL = 30_000; // 30 seconds\nconst DEFAULT_CACHE_KEY = \"frak-config-cache\";\nconst MERCHANT_ID_KEY = \"frak-merchant-id\";\n\nconst cacheState = { key: DEFAULT_CACHE_KEY };\n\nconst isBrowser = typeof window !== \"undefined\";\n\ntype CacheEntry = { config: SdkResolvedConfig; timestamp: number };\n\ndeclare global {\n interface Window {\n [GLOBAL_KEY]?: SdkResolvedConfig;\n }\n interface WindowEventMap {\n \"frak:config\": CustomEvent<SdkResolvedConfig>;\n }\n}\n\nfunction freshEmptyConfig(): SdkResolvedConfig {\n return { isResolved: false, merchantId: \"\" };\n}\n\n// ---------------------------------------------------------------------------\n// localStorage cache (with in-memory parsed copy)\n// ---------------------------------------------------------------------------\n\nlet memoryEntry: CacheEntry | null = null;\n\nfunction loadCacheEntry(): CacheEntry | null {\n if (!isBrowser) return null;\n try {\n const raw = localStorage.getItem(cacheState.key);\n if (!raw) return null;\n const entry: CacheEntry = JSON.parse(raw);\n if (!entry.config?.isResolved) return null;\n memoryEntry = entry;\n return entry;\n } catch {\n return null;\n }\n}\n\nfunction readCache(): SdkResolvedConfig | undefined {\n return (memoryEntry ?? loadCacheEntry())?.config;\n}\n\nfunction isCacheFresh(): boolean {\n const entry = memoryEntry ?? loadCacheEntry();\n if (!entry) return false;\n return Date.now() - entry.timestamp < CACHE_TTL;\n}\n\nfunction writeCache(config: SdkResolvedConfig): void {\n if (!isBrowser || !config.isResolved) return;\n try {\n const entry: CacheEntry = { config, timestamp: Date.now() };\n localStorage.setItem(cacheState.key, JSON.stringify(entry));\n memoryEntry = entry;\n } catch {}\n}\n\nfunction removeCache(): void {\n if (!isBrowser) return;\n memoryEntry = null;\n try {\n localStorage.removeItem(cacheState.key);\n } catch {}\n}\n\n// ---------------------------------------------------------------------------\n// Initialise window-backed config (lazily, on first access)\n// ---------------------------------------------------------------------------\n\n// Seed `window.__frakSdkConfig` from the localStorage cache. Do NOT call this at\n// module top-level: that side effect contradicts `sideEffects: false` (a bundler\n// may legally drop it) and forces a localStorage read on every consumer at import.\n// It is invoked lazily from `getConfig` instead.\nfunction initConfig(): void {\n if (!isBrowser) return;\n if (window[GLOBAL_KEY]) return;\n window[GLOBAL_KEY] = readCache() ?? freshEmptyConfig();\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction getConfig(): SdkResolvedConfig {\n if (!isBrowser) return freshEmptyConfig();\n initConfig();\n return window[GLOBAL_KEY] ?? freshEmptyConfig();\n}\n\nfunction dispatch(config: SdkResolvedConfig): void {\n if (!isBrowser) return;\n window.dispatchEvent(new CustomEvent(\"frak:config\", { detail: config }));\n}\n\nfunction getTargetDomain(domain?: string): string {\n return domain ?? (isBrowser ? window.location.hostname : \"\");\n}\n\n// ---------------------------------------------------------------------------\n// Merchant config fetching (resolve)\n// ---------------------------------------------------------------------------\n\nasync function fetchFromBackend(\n targetDomain: string,\n walletUrl?: string,\n lang?: Language\n): Promise<MerchantConfigResponse | undefined> {\n try {\n const backendUrl = getBackendUrl(walletUrl);\n const langParam = lang ? `&lang=${encodeURIComponent(lang)}` : \"\";\n const response = await fetch(\n `${backendUrl}/user/merchant/resolve?domain=${encodeURIComponent(targetDomain)}${langParam}`\n );\n\n if (!response.ok) {\n console.warn(\n `[Frak SDK] Merchant lookup failed for domain ${targetDomain}: ${response.status}`\n );\n return undefined;\n }\n\n const data = (await response.json()) as MerchantConfigResponse;\n\n // Write compatibility sessionStorage key\n if (isBrowser) {\n try {\n sessionStorage.setItem(MERCHANT_ID_KEY, data.merchantId);\n } catch {}\n }\n\n return data;\n } catch (error) {\n console.warn(\"[Frak SDK] Failed to fetch merchant config:\", error);\n return undefined;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport const sdkConfigStore = {\n getConfig,\n\n get isResolved(): boolean {\n return getConfig().isResolved;\n },\n\n get isCacheFresh(): boolean {\n return isCacheFresh();\n },\n\n setCacheScope(domain: string, lang?: string): void {\n const suffix = `${domain}:${lang ?? \"\"}`;\n cacheState.key = `${DEFAULT_CACHE_KEY}:${suffix}`;\n memoryEntry = null;\n },\n\n setConfig(config: SdkResolvedConfig): void {\n if (isBrowser) window[GLOBAL_KEY] = config;\n writeCache(config);\n dispatch(config);\n\n // Keep sessionStorage merchantId in sync\n if (isBrowser && config.merchantId) {\n try {\n sessionStorage.setItem(MERCHANT_ID_KEY, config.merchantId);\n } catch {}\n }\n },\n\n reset(): void {\n const next = readCache() ?? freshEmptyConfig();\n if (isBrowser) window[GLOBAL_KEY] = next;\n dispatch(next);\n },\n\n clearCache(): void {\n removeCache();\n clearAllCache();\n if (isBrowser) {\n try {\n sessionStorage.removeItem(MERCHANT_ID_KEY);\n } catch {}\n }\n },\n\n resolve(\n domain?: string,\n walletUrl?: string,\n lang?: Language\n ): Promise<MerchantConfigResponse | undefined> {\n const targetDomain = getTargetDomain(domain);\n if (!targetDomain) {\n return Promise.resolve(undefined);\n }\n\n const cacheKey = `sdkConfig:${targetDomain}:${lang ?? \"\"}`;\n\n return withCache(\n async () => {\n const result = await fetchFromBackend(\n targetDomain,\n walletUrl,\n lang\n );\n // Throw on failure so withCache doesn't cache undefined\n if (!result) {\n throw new Error(\"Config resolution returned empty\");\n }\n return result;\n },\n { cacheKey, cacheTime: Number.POSITIVE_INFINITY }\n ).catch(() => undefined);\n },\n\n getMerchantId(): string | undefined {\n const config = getConfig();\n if (config.isResolved && config.merchantId) {\n return config.merchantId;\n }\n\n if (isBrowser) {\n try {\n return sessionStorage.getItem(MERCHANT_ID_KEY) ?? undefined;\n } catch {}\n }\n return undefined;\n },\n\n async resolveMerchantId(\n domain?: string,\n walletUrl?: string\n ): Promise<string | undefined> {\n const fast = sdkConfigStore.getMerchantId();\n if (fast) return fast;\n\n const config = await sdkConfigStore.resolve(domain, walletUrl);\n return config?.merchantId;\n },\n};\n","/**\n * Encode a buffer to a base64url encoded string\n * @param buffer The buffer to encode\n * @returns The encoded string\n */\nexport function base64urlEncode(buffer: Uint8Array): string {\n return btoa(Array.from(buffer, (b) => String.fromCharCode(b)).join(\"\"))\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\n/**\n * Decode a base64url encoded string\n * @param value The value to decode\n * @returns The decoded value\n */\nexport function base64urlDecode(value: string): Uint8Array {\n const m = value.length % 4;\n return Uint8Array.from(\n atob(\n value\n .replace(/-/g, \"+\")\n .replace(/_/g, \"/\")\n .padEnd(value.length + (m === 0 ? 0 : 4 - m), \"=\")\n ),\n (c) => c.charCodeAt(0)\n );\n}\n","import { jsonEncode } from \"@frak-labs/frame-connector\";\nimport { base64urlEncode } from \"./b64\";\n\n/**\n * Compress json data\n * @param data\n * @ignore\n */\nexport function compressJsonToB64(data: unknown): string {\n return base64urlEncode(jsonEncode(data));\n}\n","import type { Hex } from \"viem\";\nimport type { PrepareSsoParamsType, SsoMetadata } from \"../../types\";\nimport { compressJsonToB64 } from \"../compression/compress\";\n\nexport type AppSpecificSsoMetadata = SsoMetadata & {\n name?: string;\n css?: string;\n};\n\n/**\n * The full SSO params that will be used for compression\n */\nexport type FullSsoParams = Omit<PrepareSsoParamsType, \"metadata\"> & {\n metadata: AppSpecificSsoMetadata;\n merchantId: string;\n clientId: string;\n};\n\n/**\n * Generate SSO URL with compressed parameters\n * This mirrors the wallet's getOpenSsoLink() function\n *\n * @param walletUrl - Base wallet URL (e.g., \"https://wallet.frak.id\")\n * @param params - SSO parameters\n * @param merchantId - Merchant identifier\n * @param name - Application name\n * @param clientId - Client identifier for identity tracking\n * @param css - Optional custom CSS\n * @returns Complete SSO URL ready to open in popup or redirect\n *\n * @example\n * ```ts\n * const ssoUrl = generateSsoUrl(\n * \"https://wallet.frak.id\",\n * { metadata: { logoUrl: \"...\" }, directExit: true },\n * \"0x123...\",\n * \"My App\"\n * );\n * // Returns: https://wallet.frak.id/sso?p=<compressed_base64>\n * ```\n */\nexport function generateSsoUrl(\n walletUrl: string,\n params: PrepareSsoParamsType,\n merchantId: string,\n name: string | undefined,\n clientId: string,\n css?: string\n): string {\n // Build full params with app-specific metadata\n const fullParams: FullSsoParams = {\n redirectUrl: params.redirectUrl,\n directExit: params.directExit,\n lang: params.lang,\n merchantId,\n metadata: {\n name,\n css,\n logoUrl: params.metadata?.logoUrl,\n homepageLink: params.metadata?.homepageLink,\n },\n clientId,\n };\n\n // Compress params to minimal format\n const compressedParam = ssoParamsToCompressed(fullParams);\n\n // Encode to base64url\n const compressedString = compressJsonToB64(compressedParam);\n\n // Build URL matching wallet's expected format: /sso?p=<compressed>\n const ssoUrl = new URL(walletUrl);\n ssoUrl.pathname = \"/sso\";\n ssoUrl.searchParams.set(\"p\", compressedString);\n\n return ssoUrl.toString();\n}\n\n/**\n * Map full sso params to compressed sso params\n * @param params\n */\nfunction ssoParamsToCompressed(params: FullSsoParams): CompressedSsoData {\n return {\n r: params.redirectUrl,\n cId: params.clientId,\n d: params.directExit,\n l: params.lang,\n m: params.merchantId,\n md: {\n n: params.metadata?.name,\n css: params.metadata?.css,\n l: params.metadata?.logoUrl,\n h: params.metadata?.homepageLink,\n },\n };\n}\n\n/**\n * Type of compressed the sso data\n */\nexport type CompressedSsoData = {\n // Potential id from backend\n id?: Hex;\n // Client id\n cId: string;\n // redirect url\n r?: string;\n // direct exit\n d?: boolean;\n // language\n l?: \"en\" | \"fr\";\n // merchant id\n m: string;\n // metadata\n md: {\n n?: string;\n css?: string;\n l?: string;\n h?: string;\n };\n};\n","import { getClientId } from \"../config/clientId\";\nimport { sdkConfigStore } from \"../config/sdkConfigStore\";\nimport type {\n FrakClient,\n OpenSsoParamsType,\n OpenSsoReturnType,\n} from \"../types\";\nimport { generateSsoUrl } from \"../utils/sso/sso\";\n\n// SSO popup configuration\nexport const ssoPopupFeatures =\n \"menubar=no,status=no,scrollbars=no,fullscreen=no,width=500, height=800\";\nexport const ssoPopupName = \"frak-sso\";\n\n/**\n * Function used to open the SSO\n * @param client - The current Frak Client\n * @param args - The SSO parameters\n *\n * @description Two SSO flow modes:\n *\n * **Redirect Mode** (openInSameWindow: true):\n * - Wallet generates URL and triggers redirect\n * - Used when redirectUrl is provided\n *\n * **Popup Mode** (openInSameWindow: false/omitted):\n * - SDK generates URL client-side (or uses provided ssoPopupUrl)\n * - Opens popup synchronously (prevents popup blockers)\n * - Waits for SSO completion via postMessage\n *\n * @example\n * First we build the sso metadata\n * ```ts\n * // Build the metadata\n * const metadata: SsoMetadata = {\n * logoUrl: \"https://my-app.com/logo.png\",\n * homepageLink: \"https://my-app.com\",\n * };\n * ```\n *\n * Then, either use it with direct exit (and so user is directly redirected to your website), or a custom redirect URL\n * :::code-group\n * ```ts [Popup (default)]\n * // Opens in popup, SDK generates URL automatically\n * await openSso(frakConfig, {\n * directExit: true,\n * metadata,\n * });\n * ```\n * ```ts [Redirect]\n * // Opens in same window with redirect\n * await openSso(frakConfig, {\n * redirectUrl: \"https://my-app.com/frak-sso\",\n * metadata,\n * openInSameWindow: true,\n * });\n * ```\n * ```ts [Custom popup URL]\n * // Advanced: provide custom SSO URL\n * const { ssoUrl } = await prepareSso(frakConfig, { metadata });\n * await openSso(frakConfig, {\n * metadata,\n * ssoPopupUrl: `${ssoUrl}&custom=param`,\n * });\n * ```\n * :::\n */\nexport async function openSso(\n client: FrakClient,\n inputArgs: OpenSsoParamsType\n): Promise<OpenSsoReturnType> {\n const { metadata, customizations, walletUrl } = client.config;\n\n // Apply default: when no redirectUrl is provided we want the SSO popup\n // to close itself after completion. Without this default the popup\n // sticks on the success screen and the \"Redirect now\" button is a no-op.\n const args: OpenSsoParamsType = {\n ...inputArgs,\n directExit: inputArgs.directExit ?? !inputArgs.redirectUrl,\n };\n\n // Check if redirect mode (default to true if redirectUrl present)\n const isRedirectMode = args.openInSameWindow ?? !!args.redirectUrl;\n if (isRedirectMode) {\n // Redirect flow: Wallet generates URL and triggers redirect via lifecycle event\n // This must happen on wallet side because only the iframe can trigger the redirect\n return await client.request({\n method: \"frak_openSso\",\n params: [args, metadata.name, customizations?.css],\n });\n }\n\n // Popup flow: Generate URL on SDK side and open synchronously\n // This ensures window.open() is called in same tick as user gesture (no popup blocker)\n\n // Step 1: Generate or use provided SSO URL\n const ssoUrl =\n args.ssoPopupUrl ??\n generateSsoUrl(\n walletUrl ?? \"https://wallet.frak.id\",\n args,\n (await sdkConfigStore.resolveMerchantId()) ?? \"\",\n metadata.name,\n getClientId(),\n customizations?.css\n );\n\n // Step 2: Open popup synchronously (critical for popup blocker prevention)\n const popup = window.open(ssoUrl, ssoPopupName, ssoPopupFeatures);\n if (!popup) {\n throw new Error(\n \"Popup was blocked. Please allow popups for this site.\"\n );\n }\n popup.focus();\n\n // Step 3: Wait for SSO completion via RPC\n // The wallet iframe will resolve this when SSO page sends sso_complete message\n const result = await client.request({\n method: \"frak_openSso\",\n params: [args, metadata.name, customizations?.css],\n });\n\n return result ?? {};\n}\n","import type { FrakClient } from \"../../types\";\nimport type { SdkEventMap } from \"./events\";\n\n/**\n * Track an analytics event via the SDK's OpenPanel instance.\n * Fire-and-forget — silently catches errors so analytics never break a\n * partner integration.\n *\n * The client must be passed explicitly because the OpenPanel instance is\n * scoped to each `FrakClient` (a partner site may hold multiple iframes).\n *\n * @param client - The Frak client instance (no-op if undefined)\n * @param event - Typed event name from the SDK event map\n * @param properties - Typed properties for the given event\n */\nexport function trackEvent<K extends keyof SdkEventMap>(\n client: FrakClient | undefined,\n event: K,\n properties?: SdkEventMap[K]\n): void {\n if (!client) {\n console.debug(\"[Frak] No client provided, skipping event tracking\");\n return;\n }\n\n try {\n client.openPanel?.track(\n event as string,\n properties as Record<string, unknown> | undefined\n );\n } catch (e) {\n console.debug(\"[Frak] Failed to track event:\", event, e);\n }\n}\n","/**\n * Case-insensitive helpers for reading URL query parameters.\n *\n * Some email tools (Klaviyo, Omnisend, Customer.io …) and a few browsers\n * lowercase the entire URL before the recipient opens it, so a mixed-case key\n * authored as `frakAction` or `fCtx` can arrive as `frakaction` / `fctx`. A\n * plain `searchParams.get(\"frakAction\")` would miss it. Matching the key\n * ignoring case keeps UI-triggering and referral params working regardless of\n * how the link was mangled in transit.\n *\n * Note: only the key is normalised. An encoded value (base64url, tokens) is not\n * recoverable if the same channel also lowercased the value itself.\n */\n\n/**\n * Read a query parameter, matching its key case-insensitively.\n *\n * An exact-case match wins when present, so a canonical link is never shadowed\n * by a mangled duplicate (`?fctx=stale&fCtx=real` resolves to `real`). Only when\n * the exact key is absent do we scan for a case-folded variant.\n *\n * @returns the param value, or `null` when no key matches.\n */\nexport function getQueryParamCaseInsensitive(\n searchParams: URLSearchParams,\n key: string\n): string | null {\n const exact = searchParams.get(key);\n if (exact !== null) return exact;\n\n const target = key.toLowerCase();\n for (const [paramKey, value] of searchParams) {\n if (paramKey.toLowerCase() === target) {\n return value;\n }\n }\n return null;\n}\n\n/**\n * Delete every query parameter whose key matches `key` case-insensitively.\n *\n * Keys are collected before deletion because mutating a `URLSearchParams`\n * while iterating it skips entries.\n */\nexport function deleteQueryParamCaseInsensitive(\n searchParams: URLSearchParams,\n key: string\n): void {\n const target = key.toLowerCase();\n const matchingKeys = [...searchParams.keys()].filter(\n (paramKey) => paramKey.toLowerCase() === target\n );\n for (const paramKey of matchingKeys) {\n searchParams.delete(paramKey);\n }\n}\n","import type { Address } from \"viem\";\n\n/**\n * Address utilities — minimal, dependency-free replacements for the subset of\n * `viem` helpers we used to import. Keeping these in-house lets the SDK ship\n * without pulling viem's checksum/keccak/error chain into the bundle.\n *\n * Scope is intentionally narrow:\n * - `isAddress`: shape-only validation (no EIP-55 checksum)\n * - `areAddressesEqual`: case-insensitive equality\n * - `addressToBytes` / `bytesToAddress`: fixed 20-byte conversion\n */\n\n/** Matches a 0x-prefixed 40-char hex string regardless of case. */\nconst ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/;\n\n/**\n * Check whether a value is a syntactically valid Ethereum address.\n *\n * This intentionally skips EIP-55 checksum validation: the SDK never produces\n * checksum-cased payloads, and downstream consumers (wallet, indexer) treat\n * addresses case-insensitively. Avoiding the checksum path drops keccak256 +\n * @noble/hashes from the bundle.\n */\nexport function isAddress(value: unknown): value is Address {\n return typeof value === \"string\" && ADDRESS_REGEX.test(value);\n}\n\n/**\n * Case-insensitive equality check for two Ethereum addresses.\n *\n * Both inputs are assumed to be syntactically valid addresses; callers that\n * receive untrusted input should validate via {@link isAddress} first.\n */\nexport function areAddressesEqual(a: Address, b: Address): boolean {\n return a.toLowerCase() === b.toLowerCase();\n}\n\n/**\n * Decode a 20-byte Ethereum address into a fixed-size Uint8Array(20).\n *\n * Throws when the input is not exactly `0x` + 40 hex chars — callers wrap\n * the call in try/catch (see {@link FrakContextManager.compress}) so any\n * malformed input degrades to a graceful undefined return.\n */\nexport function addressToBytes(address: Address): Uint8Array {\n const bytes = new Uint8Array(20);\n for (let i = 0; i < 20; i++) {\n const byte = Number.parseInt(\n address.substring(2 + i * 2, 4 + i * 2),\n 16\n );\n if (Number.isNaN(byte)) {\n throw new Error(`Invalid address: ${address}`);\n }\n bytes[i] = byte;\n }\n return bytes;\n}\n\n/** Lookup table avoids `padStart` overhead in the hot encode loop. */\nconst HEX_BYTE = /*#__PURE__*/ Array.from({ length: 256 }, (_, i) =>\n i.toString(16).padStart(2, \"0\")\n);\n\n/**\n * Encode a 20-byte Uint8Array (or a 20-byte subarray view) into a lowercase\n * hex Ethereum address. The caller MUST guarantee `bytes.length === 20`.\n */\nexport function bytesToAddress(bytes: Uint8Array): Address {\n let out = \"0x\";\n for (let i = 0; i < 20; i++) {\n out += HEX_BYTE[bytes[i]];\n }\n return out as Address;\n}\n","import type { Address } from \"viem\";\n\n/**\n * V1 (legacy) Frak Context — contains only the referrer wallet address.\n * Used for backward compatibility with old sharing links.\n * @ignore\n */\nexport type FrakContextV1 = {\n /** Referrer wallet address */\n r: Address;\n};\n\n/**\n * V2 Frak Context — anonymous-first referral context with optional wallet.\n *\n * Carries merchant context (`m`) and creation timestamp (`t`) unconditionally.\n * Identifies the sharer via either the anonymous clientId (`c`) or, when the\n * sharer is authenticated, the stronger wallet identifier (`w`). A valid V2\n * context MUST contain at least one of `c` or `w`; both may be present when\n * a logged-in user shares a link (best attribution signal).\n *\n * `w` takes precedence as the source of truth because the wallet is bound to\n * the user's WebAuthn credential, survives localStorage clears, and is global\n * across merchants — unlike `c`, which is a per-browser UUID.\n *\n * @ignore\n */\nexport type FrakContextV2 = {\n /** Version discriminator */\n v: 2;\n /** Merchant ID (UUID) */\n m: string;\n /** Link creation timestamp (epoch seconds) */\n t: number;\n /** Sharer's anonymous clientId (UUID from localStorage). Optional when `w` is provided. */\n c?: string;\n /** Sharer's wallet address. Preferred source of truth when the sharer is authenticated. Optional when `c` is provided. */\n w?: Address;\n};\n\n/**\n * The current Frak Context — union of all versions.\n *\n * - No `v` field → V1 (legacy wallet address)\n * - `v: 2` → V2 (anonymous clientId-based)\n *\n * @ignore\n */\nexport type FrakContext = FrakContextV1 | FrakContextV2;\n\n/**\n * Type guard: check if a context is V1 (legacy wallet address).\n * @param ctx - The Frak context to check\n * @returns True if the context is a V1 context\n */\nexport function isV1Context(ctx: FrakContext): ctx is FrakContextV1 {\n return \"r\" in ctx && !(\"v\" in ctx);\n}\n\n/**\n * Type guard: check if a context is V2 (anonymous clientId-based).\n * @param ctx - The Frak context to check\n * @returns True if the context is a V2 context\n */\nexport function isV2Context(ctx: FrakContext): ctx is FrakContextV2 {\n return \"v\" in ctx && ctx.v === 2;\n}\n","/**\n * Binary codec for {@link FrakContextV2}.\n *\n * Produces a compact, URL-safe byte layout (~65% smaller than the previous\n * JSON+base64url format). See the layout below.\n *\n * ## Wire layout\n *\n * ```text\n * byte 0: header\n * bits 0-3 version (= 2)\n * bit 4 has_c flag\n * bit 5 has_w flag\n * bits 6-7 reserved (must be 0)\n * bytes 1..16: merchant UUID (16 bytes, mandatory)\n * bytes 17..20: timestamp (uint32 big-endian, Unix seconds)\n * bytes 21..36: client UUID (16 bytes, only when has_c is set)\n * bytes 37..56: wallet address (20 bytes, only when has_w is set)\n * ```\n *\n * Size variants (before base64url):\n * - has_c only: 37 bytes\n * - has_w only: 41 bytes\n * - has_c + has_w: 57 bytes\n *\n * V1 payloads are exactly 20 bytes (raw wallet address); the byte lengths never\n * overlap, so the outer decoder can disambiguate purely on length.\n *\n * @ignore\n */\nimport type { Address } from \"viem\";\nimport type { FrakContextV2 } from \"../types\";\nimport { addressToBytes, bytesToAddress, isAddress } from \"./address\";\n\nconst VERSION_V2 = 0x02;\nconst VERSION_MASK = 0x0f;\nconst FLAG_HAS_C = 1 << 4;\nconst FLAG_HAS_W = 1 << 5;\nconst RESERVED_MASK = 0xc0;\n\nconst UUID_BYTES = 16;\nconst TIMESTAMP_BYTES = 4;\nconst ADDRESS_BYTES = 20;\nconst HEADER_BYTES = 1;\n\nconst UUID_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Strict lower-case UUID validation (RFC 4122 shape, any version/variant). */\nfunction isUuid(value: unknown): value is string {\n return typeof value === \"string\" && UUID_RE.test(value);\n}\n\n/** Parse a canonical UUID string into 16 raw bytes. */\nfunction uuidToBytes(uuid: string): Uint8Array {\n const hex = uuid.replace(/-/g, \"\");\n const out = new Uint8Array(UUID_BYTES);\n for (let i = 0; i < UUID_BYTES; i++) {\n out[i] = Number.parseInt(hex.substring(i * 2, i * 2 + 2), 16);\n }\n return out;\n}\n\n/** Format 16 raw bytes as a canonical 8-4-4-4-12 UUID string. */\nfunction bytesToUuid(bytes: Uint8Array): string {\n let hex = \"\";\n for (let i = 0; i < UUID_BYTES; i++) {\n hex += bytes[i].toString(16).padStart(2, \"0\");\n }\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;\n}\n\n/**\n * Encode a {@link FrakContextV2} into its binary wire format.\n *\n * Returns `null` when the context fails runtime validation (missing fields,\n * malformed UUIDs, timestamp outside uint32 range, invalid wallet).\n */\nexport function encodeFrakContextV2(ctx: FrakContextV2): Uint8Array | null {\n if (!isUuid(ctx.m)) return null;\n if (!Number.isInteger(ctx.t) || ctx.t < 0 || ctx.t > 0xff_ff_ff_ff)\n return null;\n\n const hasC = typeof ctx.c === \"string\" && ctx.c.length > 0;\n const hasW = typeof ctx.w === \"string\" && isAddress(ctx.w);\n if (!hasC && !hasW) return null;\n if (hasC && !isUuid(ctx.c)) return null;\n\n const size =\n HEADER_BYTES +\n UUID_BYTES +\n TIMESTAMP_BYTES +\n (hasC ? UUID_BYTES : 0) +\n (hasW ? ADDRESS_BYTES : 0);\n\n const buf = new Uint8Array(size);\n const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);\n let offset = 0;\n\n buf[offset++] =\n VERSION_V2 | (hasC ? FLAG_HAS_C : 0) | (hasW ? FLAG_HAS_W : 0);\n\n buf.set(uuidToBytes(ctx.m), offset);\n offset += UUID_BYTES;\n\n view.setUint32(offset, ctx.t, false);\n offset += TIMESTAMP_BYTES;\n\n if (hasC) {\n buf.set(uuidToBytes(ctx.c as string), offset);\n offset += UUID_BYTES;\n }\n\n if (hasW) {\n buf.set(addressToBytes(ctx.w as Address), offset);\n offset += ADDRESS_BYTES;\n }\n\n return buf;\n}\n\n/**\n * Decode a binary {@link FrakContextV2} payload.\n *\n * Returns `null` when:\n * - the header version nibble is not V2\n * - reserved header bits are set (guards against future-version payloads)\n * - neither flag is set (invalid: V2 must carry `c` and/or `w`)\n * - the byte length does not match the length implied by the header flags\n * - the decoded wallet does not pass `isAddress` (defense-in-depth against\n * crafted payloads that round-trip by length but carry junk)\n */\nexport function decodeFrakContextV2(buf: Uint8Array): FrakContextV2 | null {\n if (buf.length < HEADER_BYTES + UUID_BYTES + TIMESTAMP_BYTES) return null;\n\n const header = buf[0];\n if ((header & VERSION_MASK) !== VERSION_V2) return null;\n if ((header & RESERVED_MASK) !== 0) return null;\n\n const hasC = (header & FLAG_HAS_C) !== 0;\n const hasW = (header & FLAG_HAS_W) !== 0;\n if (!hasC && !hasW) return null;\n\n const expected =\n HEADER_BYTES +\n UUID_BYTES +\n TIMESTAMP_BYTES +\n (hasC ? UUID_BYTES : 0) +\n (hasW ? ADDRESS_BYTES : 0);\n if (buf.length !== expected) return null;\n\n let offset = HEADER_BYTES;\n\n const m = bytesToUuid(buf.subarray(offset, offset + UUID_BYTES));\n offset += UUID_BYTES;\n\n const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);\n const t = view.getUint32(offset, false);\n offset += TIMESTAMP_BYTES;\n\n const out: FrakContextV2 = { v: 2, m, t };\n\n if (hasC) {\n out.c = bytesToUuid(buf.subarray(offset, offset + UUID_BYTES));\n offset += UUID_BYTES;\n }\n\n if (hasW) {\n const walletHex = bytesToAddress(\n buf.subarray(offset, offset + ADDRESS_BYTES)\n );\n if (!isAddress(walletHex)) return null;\n out.w = walletHex;\n offset += ADDRESS_BYTES;\n }\n\n return out;\n}\n\n/**\n * Quick length-based probe to tell V1 (20-byte wallet address) apart from a V2\n * binary payload. Exposed so the outer decoder can branch without re-parsing.\n */\nexport function isV2BinaryLength(byteLength: number): boolean {\n return (\n byteLength ===\n HEADER_BYTES + UUID_BYTES + TIMESTAMP_BYTES + UUID_BYTES ||\n byteLength ===\n HEADER_BYTES + UUID_BYTES + TIMESTAMP_BYTES + ADDRESS_BYTES ||\n byteLength ===\n HEADER_BYTES +\n UUID_BYTES +\n TIMESTAMP_BYTES +\n UUID_BYTES +\n ADDRESS_BYTES\n );\n}\n","import type {\n AttributionParams,\n FrakContext,\n FrakContextV1,\n FrakContextV2,\n} from \"../types\";\nimport { isV2Context } from \"../types\";\nimport { base64urlDecode, base64urlEncode } from \"../utils/compression/b64\";\nimport {\n deleteQueryParamCaseInsensitive,\n getQueryParamCaseInsensitive,\n} from \"../utils/url/queryParams\";\nimport { addressToBytes, bytesToAddress, isAddress } from \"./address\";\nimport { decodeFrakContextV2, encodeFrakContextV2 } from \"./frakContextV2Codec\";\n\n/**\n * URL parameter key for the Frak referral context\n */\nconst contextKey = \"fCtx\";\n\n/**\n * Compress a Frak context into a URL-safe string.\n *\n * - V2 contexts are encoded using a compact binary layout (see\n * {@link encodeFrakContextV2}) then base64url-encoded.\n * - V1 contexts encode the wallet address as raw bytes (base64url).\n *\n * @param context - The context to compress (V1 or V2)\n * @returns A compressed base64url string, or undefined on failure\n */\nfunction compress(context?: FrakContextV1 | FrakContextV2): string | undefined {\n if (!context) return;\n try {\n if (isV2Context(context)) {\n const encoded = encodeFrakContextV2(context);\n if (!encoded) return undefined;\n return base64urlEncode(encoded);\n }\n\n // V1 legacy: compress wallet address as raw bytes\n const bytes = addressToBytes(context.r);\n return base64urlEncode(bytes);\n } catch (e) {\n console.error(\"Error compressing Frak context\", { e, context });\n }\n return undefined;\n}\n\n/**\n * Decompress a base64url string back into a Frak context.\n *\n * V1 (exactly 20 bytes) and V2 (37, 41, or 57 bytes) are distinguished by\n * their decoded byte length, so there is no ambiguity.\n *\n * @param context - The compressed context string\n * @returns The decompressed FrakContext, or undefined on failure\n */\nfunction decompress(context?: string): FrakContext | undefined {\n if (!context || context.length === 0) return;\n try {\n const bytes = base64urlDecode(context);\n\n // V1 is a raw 20-byte wallet address; V2 binary is always longer\n // and starts with a header byte whose low nibble is the version.\n if (bytes.length !== 20) {\n const v2 = decodeFrakContextV2(bytes);\n if (v2) return v2;\n return undefined;\n }\n\n const hex = bytesToAddress(bytes);\n if (isAddress(hex)) {\n return { r: hex };\n }\n } catch (e) {\n console.error(\"Error decompressing Frak context\", { e, context });\n }\n return undefined;\n}\n\n/**\n * Parse a URL to extract the Frak referral context from the `fCtx` query parameter.\n *\n * The key is matched case-insensitively: some link channels (emails, messaging\n * apps) lowercase query-param keys in transit, so `fCtx` can arrive as `fctx`.\n *\n * @param args\n * @param args.url - The URL to parse\n * @returns The parsed FrakContext, or null if absent\n */\nfunction parse({ url }: { url: string }): FrakContext | null | undefined {\n if (!url) return null;\n\n const urlObj = new URL(url);\n const frakContext = getQueryParamCaseInsensitive(\n urlObj.searchParams,\n contextKey\n );\n if (!frakContext) return null;\n\n return decompress(frakContext);\n}\n\n/**\n * Default utm_source / via value when attribution is requested.\n */\nconst DEFAULT_ATTRIBUTION_SOURCE = \"frak\";\n\n/**\n * Resolve attribution defaults from the provided context.\n *\n * V2 contexts expose the merchantId (`m`) and, when anonymous, the clientId\n * (`c`), which feed `utm_campaign` and `ref` respectively. When V2 only carries\n * a wallet (`w`), `ref` is intentionally left unset — we don't want wallet\n * addresses leaking into UTM params. V1 contexts have no equivalent.\n */\nfunction resolveAttributionValues(\n overrides: AttributionParams\n): Record<string, string | undefined> {\n return {\n utm_source: overrides.utmSource ?? DEFAULT_ATTRIBUTION_SOURCE,\n utm_medium: overrides.utmMedium ?? undefined,\n utm_campaign: overrides.utmCampaign ?? undefined,\n utm_content: overrides.utmContent,\n utm_term: overrides.utmTerm,\n via: overrides.via ?? undefined,\n ref: overrides.ref ?? undefined,\n };\n}\n\n/**\n * Append attribution query params to a URL using gap-fill semantics.\n *\n * Existing params on the URL are preserved untouched (so merchant-provided\n * UTMs take precedence). Only missing keys are populated.\n */\nfunction applyAttributionParams(\n urlObj: URL,\n attribution?: AttributionParams\n): void {\n const values = resolveAttributionValues(attribution ?? {});\n for (const [key, value] of Object.entries(values)) {\n if (value === undefined || value === \"\") continue;\n if (urlObj.searchParams.has(key)) continue;\n urlObj.searchParams.set(key, value);\n }\n}\n\n/**\n * Add or replace the `fCtx` query parameter in a URL with the given context.\n *\n * Standard affiliation params (`utm_source`, `utm_medium`, `utm_campaign`,\n * `ref`, `via`, ...) are always appended using gap-fill semantics: pre-existing\n * params on the URL are preserved, defaults are derived from the context when\n * applicable, and `attribution` overrides take precedence when provided.\n *\n * @param args\n * @param args.url - The URL to update\n * @param args.context - The context to embed (V1 or V2)\n * @param args.attribution - Optional attribution overrides. Defaults are applied even when omitted.\n * @returns The updated URL string, or null on failure\n */\nfunction update({\n url,\n context,\n attribution,\n}: {\n url?: string;\n context: FrakContextV1 | FrakContextV2;\n attribution?: AttributionParams;\n}): string | null {\n if (!url) return null;\n\n const compressedContext = compress(context);\n if (!compressedContext) return null;\n\n const urlObj = new URL(url);\n deleteQueryParamCaseInsensitive(urlObj.searchParams, contextKey);\n urlObj.searchParams.set(contextKey, compressedContext);\n applyAttributionParams(urlObj, attribution);\n return urlObj.toString();\n}\n\n/**\n * Remove the `fCtx` query parameter from a URL.\n *\n * @param url - The URL to strip the context from\n * @returns The cleaned URL string\n */\nfunction remove(url: string): string {\n const urlObj = new URL(url);\n deleteQueryParamCaseInsensitive(urlObj.searchParams, contextKey);\n return urlObj.toString();\n}\n\n/**\n * Replace the current browser URL with an updated Frak context.\n *\n * - If `context` is non-null, embeds it via {@link update}.\n * - If `context` is null, strips the context via {@link remove}.\n *\n * @param args\n * @param args.url - Base URL (defaults to `window.location.href`)\n * @param args.context - Context to set, or null to remove\n */\nfunction replaceUrl({\n url: baseUrl,\n context,\n}: {\n url?: string;\n context: FrakContextV1 | FrakContextV2 | null;\n}) {\n if (!window.location?.href || typeof window === \"undefined\") {\n console.error(\"No window found, can't update context\");\n return;\n }\n\n const url = baseUrl ?? window.location.href;\n\n let newUrl: string | null;\n if (context !== null) {\n newUrl = update({ url, context });\n } else {\n newUrl = remove(url);\n }\n\n if (!newUrl) return;\n\n window.history.replaceState(null, \"\", newUrl.toString());\n}\n\n/**\n * Manager for Frak referral context in URLs.\n *\n * Handles compression, decompression, URL parsing, and browser history updates\n * for both V1 (wallet address) and V2 (anonymous clientId) referral contexts.\n */\nexport const FrakContextManager = {\n compress,\n decompress,\n parse,\n update,\n remove,\n replaceUrl,\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
let e=require(`@frak-labs/frame-connector`);const t=`frak-client-id`;function n(){return typeof crypto<`u`&&crypto.randomUUID?crypto.randomUUID():`xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e===`x`?t:t&3|8).toString(16)})}function r(){if(typeof window>`u`||!window.localStorage)return console.warn(`[Frak SDK] No Window / localStorage available to save the clientId`),n();let e=localStorage.getItem(t);return e||(e=n(),localStorage.setItem(t,e)),e}var i=class extends Map{maxSize;constructor(e){super(),this.maxSize=e}get(e){let t=super.get(e);return super.has(e)&&(super.delete(e),super.set(e,t)),t}set(e,t){if(super.has(e)&&super.delete(e),super.set(e,t),this.maxSize&&this.size>this.maxSize){let e=super.keys().next().value;e!==void 0&&super.delete(e)}return this}};let a;function o(){return a||={promiseCache:new i(1024),responseCache:new i(1024),failureCache:new i(1024)},a}async function s(e,{cacheKey:t,cacheTime:n=3e4}){let{promiseCache:r,responseCache:i,failureCache:a}=o();if(n>0){let e=i.get(t);if(e&&Date.now()-e.created<n)return e.data}let s=a.get(t);if(s&&Date.now()-s<1e3)throw Error(`Cache: ${t} recently failed, backing off`);let c=r.get(t);c||(c=e(),r.set(t,c));try{let e=await c;return i.set(t,{data:e,created:Date.now()}),a.delete(t),e}catch(e){throw a.set(t,Date.now()),e}finally{r.delete(t)}}function c(){a&&(a.promiseCache.clear(),a.responseCache.clear(),a.failureCache.clear())}const l=`https://backend.frak.id`;function u(e){return e.includes(`localhost:3000`)||e.includes(`localhost:3010`)}function d(e){return u(e)?`https://localhost:3030`:e.includes(`wallet-dev.frak.id`)||e.includes(`wallet.gcp-dev.frak.id`)?`https://backend.gcp-dev.frak.id`:l}function f(e){if(e)return d(e);if(typeof window<`u`){let e=window.FrakSetup?.client?.config?.walletUrl;if(e)return d(e)}return l}const p=`__frakSdkConfig`,m=`frak-config-cache`,h=`frak-merchant-id`,g={key:m},_=typeof window<`u`;function v(){return{isResolved:!1,merchantId:``}}let y=null;function b(){if(!_)return null;try{let e=localStorage.getItem(g.key);if(!e)return null;let t=JSON.parse(e);return t.config?.isResolved?(y=t,t):null}catch{return null}}function x(){return(y??b())?.config}function ee(){let e=y??b();return e?Date.now()-e.timestamp<3e4:!1}function S(e){if(!(!_||!e.isResolved))try{let t={config:e,timestamp:Date.now()};localStorage.setItem(g.key,JSON.stringify(t)),y=t}catch{}}function C(){if(_){y=null;try{localStorage.removeItem(g.key)}catch{}}}function w(){_&&(window[p]||(window[p]=x()??v()))}function T(){return _?(w(),window[p]??v()):v()}function E(e){_&&window.dispatchEvent(new CustomEvent(`frak:config`,{detail:e}))}function te(e){return e??(_?window.location.hostname:``)}async function ne(e,t,n){try{let r=f(t),i=n?`&lang=${encodeURIComponent(n)}`:``,a=await fetch(`${r}/user/merchant/resolve?domain=${encodeURIComponent(e)}${i}`);if(!a.ok){console.warn(`[Frak SDK] Merchant lookup failed for domain ${e}: ${a.status}`);return}let o=await a.json();if(_)try{sessionStorage.setItem(h,o.merchantId)}catch{}return o}catch(e){console.warn(`[Frak SDK] Failed to fetch merchant config:`,e);return}}const D={getConfig:T,get isResolved(){return T().isResolved},get isCacheFresh(){return ee()},setCacheScope(e,t){g.key=`${m}:${`${e}:${t??``}`}`,y=null},setConfig(e){if(_&&(window[p]=e),S(e),E(e),_&&e.merchantId)try{sessionStorage.setItem(h,e.merchantId)}catch{}},reset(){let e=x()??v();_&&(window[p]=e),E(e)},clearCache(){if(C(),c(),_)try{sessionStorage.removeItem(h)}catch{}},resolve(e,t,n){let r=te(e);return r?s(async()=>{let e=await ne(r,t,n);if(!e)throw Error(`Config resolution returned empty`);return e},{cacheKey:`sdkConfig:${r}:${n??``}`,cacheTime:1/0}).catch(()=>void 0):Promise.resolve(void 0)},getMerchantId(){let e=T();if(e.isResolved&&e.merchantId)return e.merchantId;if(_)try{return sessionStorage.getItem(h)??void 0}catch{}},async resolveMerchantId(e,t){return D.getMerchantId()||(await D.resolve(e,t))?.merchantId}};function O(e){return btoa(Array.from(e,e=>String.fromCharCode(e)).join(``)).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``)}function k(e){let t=e.length%4;return Uint8Array.from(atob(e.replace(/-/g,`+`).replace(/_/g,`/`).padEnd(e.length+(t===0?0:4-t),`=`)),e=>e.charCodeAt(0))}function A(t){return O((0,e.jsonEncode)(t))}function j(e,t,n,r,i,a){let o=A(re({redirectUrl:t.redirectUrl,directExit:t.directExit,lang:t.lang,merchantId:n,metadata:{name:r,css:a,logoUrl:t.metadata?.logoUrl,homepageLink:t.metadata?.homepageLink},clientId:i})),s=new URL(e);return s.pathname=`/sso`,s.searchParams.set(`p`,o),s.toString()}function re(e){return{r:e.redirectUrl,cId:e.clientId,d:e.directExit,l:e.lang,m:e.merchantId,md:{n:e.metadata?.name,css:e.metadata?.css,l:e.metadata?.logoUrl,h:e.metadata?.homepageLink}}}const M=`menubar=no,status=no,scrollbars=no,fullscreen=no,width=500, height=800`,N=`frak-sso`;async function P(e,t){let{metadata:n,customizations:i,walletUrl:a}=e.config,o={...t,directExit:t.directExit??!t.redirectUrl};if(o.openInSameWindow??!!o.redirectUrl)return await e.request({method:`frak_openSso`,params:[o,n.name,i?.css]});let s=o.ssoPopupUrl??j(a??`https://wallet.frak.id`,o,await D.resolveMerchantId()??``,n.name,r(),i?.css),c=window.open(s,N,M);if(!c)throw Error(`Popup was blocked. Please allow popups for this site.`);return c.focus(),await e.request({method:`frak_openSso`,params:[o,n.name,i?.css]})??{}}function F(e,t,n){if(!e){console.debug(`[Frak] No client provided, skipping event tracking`);return}try{e.openPanel?.track(t,n)}catch(e){console.debug(`[Frak] Failed to track event:`,t,e)}}function I(e,t){let n=e.get(t);if(n!==null)return n;let r=t.toLowerCase();for(let[t,n]of e)if(t.toLowerCase()===r)return n;return null}function L(e,t){let n=t.toLowerCase(),r=[...e.keys()].filter(e=>e.toLowerCase()===n);for(let t of r)e.delete(t)}const R=/^0x[a-fA-F0-9]{40}$/;function z(e){return typeof e==`string`&&R.test(e)}function ie(e,t){return e.toLowerCase()===t.toLowerCase()}function B(e){let t=new Uint8Array(20);for(let n=0;n<20;n++){let r=Number.parseInt(e.substring(2+n*2,4+n*2),16);if(Number.isNaN(r))throw Error(`Invalid address: ${e}`);t[n]=r}return t}const V=Array.from({length:256},(e,t)=>t.toString(16).padStart(2,`0`));function H(e){let t=`0x`;for(let n=0;n<20;n++)t+=V[e[n]];return t}function U(e){return`r`in e&&!(`v`in e)}function W(e){return`v`in e&&e.v===2}const G=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function K(e){return typeof e==`string`&&G.test(e)}function q(e){let t=e.replace(/-/g,``),n=new Uint8Array(16);for(let e=0;e<16;e++)n[e]=Number.parseInt(t.substring(e*2,e*2+2),16);return n}function J(e){let t=``;for(let n=0;n<16;n++)t+=e[n].toString(16).padStart(2,`0`);return`${t.slice(0,8)}-${t.slice(8,12)}-${t.slice(12,16)}-${t.slice(16,20)}-${t.slice(20,32)}`}function ae(e){if(!K(e.m)||!Number.isInteger(e.t)||e.t<0||e.t>4294967295)return null;let t=typeof e.c==`string`&&e.c.length>0,n=typeof e.w==`string`&&z(e.w);if(!t&&!n||t&&!K(e.c))return null;let r=new Uint8Array(21+(t?16:0)+(n?20:0)),i=new DataView(r.buffer,r.byteOffset,r.byteLength),a=0;return r[a++]=2|(t?16:0)|(n?32:0),r.set(q(e.m),a),a+=16,i.setUint32(a,e.t,!1),a+=4,t&&(r.set(q(e.c),a),a+=16),n&&(r.set(B(e.w),a),a+=20),r}function oe(e){if(e.length<21)return null;let t=e[0];if((t&15)!=2||t&192)return null;let n=(t&16)!=0,r=(t&32)!=0;if(!n&&!r)return null;let i=21+(n?16:0)+(r?20:0);if(e.length!==i)return null;let a=1,o=J(e.subarray(a,a+16));a+=16;let s=new DataView(e.buffer,e.byteOffset,e.byteLength).getUint32(a,!1);a+=4;let c={v:2,m:o,t:s};if(n&&(c.c=J(e.subarray(a,a+16)),a+=16),r){let t=H(e.subarray(a,a+20));if(!z(t))return null;c.w=t,a+=20}return c}const Y=`fCtx`;function X(e){if(e)try{if(W(e)){let t=ae(e);return t?O(t):void 0}return O(B(e.r))}catch(t){console.error(`Error compressing Frak context`,{e:t,context:e})}}function Z(e){if(!(!e||e.length===0))try{let t=k(e);if(t.length!==20)return oe(t)||void 0;let n=H(t);if(z(n))return{r:n}}catch(t){console.error(`Error decompressing Frak context`,{e:t,context:e})}}function se({url:e}){if(!e)return null;let t=I(new URL(e).searchParams,Y);return t?Z(t):null}function ce(e){return{utm_source:e.utmSource??`frak`,utm_medium:e.utmMedium??void 0,utm_campaign:e.utmCampaign??void 0,utm_content:e.utmContent,utm_term:e.utmTerm,via:e.via??void 0,ref:e.ref??void 0}}function le(e,t){let n=ce(t??{});for(let[t,r]of Object.entries(n))r===void 0||r===``||e.searchParams.has(t)||e.searchParams.set(t,r)}function Q({url:e,context:t,attribution:n}){if(!e)return null;let r=X(t);if(!r)return null;let i=new URL(e);return L(i.searchParams,Y),i.searchParams.set(Y,r),le(i,n),i.toString()}function $(e){let t=new URL(e);return L(t.searchParams,Y),t.toString()}function ue({url:e,context:t}){if(!window.location?.href||typeof window>`u`){console.error(`No window found, can't update context`);return}let n=e??window.location.href,r;r=t===null?$(n):Q({url:n,context:t}),r&&window.history.replaceState(null,``,r.toString())}const de={compress:X,decompress:Z,parse:se,update:Q,remove:$,replaceUrl:ue};Object.defineProperty(exports,`_`,{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return L}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return P}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,`g`,{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,`h`,{enumerable:!0,get:function(){return D}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return ie}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,`m`,{enumerable:!0,get:function(){return O}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return U}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return I}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return W}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return F}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return de}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return N}}),Object.defineProperty(exports,`v`,{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,`y`,{enumerable:!0,get:function(){return r}});
|
|
2
|
+
//# sourceMappingURL=frakContext-CeVlWFcX.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frakContext-CeVlWFcX.cjs","names":[],"sources":["../src/config/clientId.ts","../src/utils/cache/lruMap.ts","../src/utils/cache/withCache.ts","../src/config/backendUrl.ts","../src/config/sdkConfigStore.ts","../src/utils/compression/b64.ts","../src/utils/compression/compress.ts","../src/utils/sso/sso.ts","../src/actions/openSso.ts","../src/utils/analytics/trackEvent.ts","../src/utils/url/queryParams.ts","../src/context/address.ts","../src/types/context.ts","../src/context/frakContextV2Codec.ts","../src/context/frakContext.ts"],"sourcesContent":["/**\n * Client ID utilities for anonymous tracking\n * Generates and persists a UUID fingerprint for referral attribution\n */\n\nconst CLIENT_ID_KEY = \"frak-client-id\";\n\n/**\n * Generate a UUID v4\n * Uses crypto.randomUUID if available, otherwise falls back to a manual implementation\n */\nfunction generateUUID(): string {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older browsers\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\n/**\n * Get the client ID from localStorage, creating one if it doesn't exist\n * @returns The client ID (UUID format)\n */\nexport function getClientId(): string {\n if (typeof window === \"undefined\" || !window.localStorage) {\n // SSR or no localStorage - generate ephemeral ID\n console.warn(\n \"[Frak SDK] No Window / localStorage available to save the clientId\"\n );\n return generateUUID();\n }\n\n let clientId = localStorage.getItem(CLIENT_ID_KEY);\n if (!clientId) {\n clientId = generateUUID();\n localStorage.setItem(CLIENT_ID_KEY, clientId);\n }\n return clientId;\n}\n","/**\n * Map with a LRU (Least Recently Used) eviction policy.\n *\n * When the map exceeds `maxSize`, the least recently accessed entry is removed.\n * Accessing a key via `get()` promotes it to \"most recently used\".\n *\n * Adapted from viem's LruMap utility.\n * @link https://en.wikipedia.org/wiki/Cache_replacement_policies#LRU\n */\nexport class LruMap<TValue = unknown> extends Map<string, TValue> {\n maxSize: number;\n\n constructor(size: number) {\n super();\n this.maxSize = size;\n }\n\n override get(key: string) {\n const value = super.get(key);\n if (super.has(key)) {\n // Move to end (most recently used)\n super.delete(key);\n super.set(key, value as TValue);\n }\n return value;\n }\n\n override set(key: string, value: TValue) {\n if (super.has(key)) super.delete(key);\n super.set(key, value);\n // Evict least recently used if over capacity\n if (this.maxSize && this.size > this.maxSize) {\n const firstKey = super.keys().next().value;\n if (firstKey !== undefined) super.delete(firstKey);\n }\n return this;\n }\n}\n","import { LruMap } from \"./lruMap\";\n\ntype CacheEntry<TData> = {\n data: TData;\n created: number;\n};\n\n/** Default cache time: 30 seconds */\nexport const DEFAULT_CACHE_TIME = 30_000;\n\n/** Short negative cache to avoid flooding on transient failures */\nconst NEGATIVE_CACHE_TIME = 1_000;\n\ntype CacheStore = {\n /** In-flight promises (dedup concurrent calls) */\n promiseCache: LruMap<Promise<unknown>>;\n /** Resolved responses (TTL-based) */\n responseCache: LruMap<CacheEntry<unknown>>;\n /** Recently failed keys to avoid request floods */\n failureCache: LruMap<number>;\n};\n\n// Allocate the three LruMaps lazily on first use rather than at module init.\n// Eager top-level allocation is a side effect at odds with `sideEffects: false`\n// and runs for every consumer that merely imports this module.\nlet store: CacheStore | undefined;\nfunction caches(): CacheStore {\n if (!store) {\n store = {\n promiseCache: new LruMap<Promise<unknown>>(1024),\n responseCache: new LruMap<CacheEntry<unknown>>(1024),\n failureCache: new LruMap<number>(1024),\n };\n }\n return store;\n}\n\ntype WithCacheOptions = {\n /** The key to cache the data against */\n cacheKey: string;\n /** Time in ms that cached data will remain valid. Default: 30_000 (30s). Set to 0 to disable caching. */\n cacheTime?: number;\n};\n\n/**\n * Returns the result of a given promise, and caches the result for\n * subsequent invocations against a provided cache key.\n *\n * Also deduplicates concurrent calls — if multiple callers request the same\n * cache key while the promise is pending, they share the same promise.\n *\n * @example\n * ```ts\n * // First call fetches, subsequent calls return cached data for 30s\n * const data = await withCache(\n * () => client.request({ method: \"frak_getMerchantInformation\" }),\n * { cacheKey: \"merchantInfo\", cacheTime: 30_000 }\n * );\n * ```\n */\nexport async function withCache<TData>(\n fn: () => Promise<TData>,\n { cacheKey, cacheTime = DEFAULT_CACHE_TIME }: WithCacheOptions\n): Promise<TData> {\n const { promiseCache, responseCache, failureCache } = caches();\n // Check response cache — return immediately if fresh\n if (cacheTime > 0) {\n const cached = responseCache.get(cacheKey) as\n | CacheEntry<TData>\n | undefined;\n if (cached) {\n const age = Date.now() - cached.created;\n if (age < cacheTime) return cached.data;\n }\n }\n\n // Check if this key recently failed — back off briefly\n const lastFailure = failureCache.get(cacheKey);\n if (lastFailure && Date.now() - lastFailure < NEGATIVE_CACHE_TIME) {\n throw new Error(`Cache: ${cacheKey} recently failed, backing off`);\n }\n\n // Check if there's already a pending promise (dedup concurrent calls)\n let promise = promiseCache.get(cacheKey) as Promise<TData> | undefined;\n if (!promise) {\n promise = fn();\n promiseCache.set(cacheKey, promise);\n }\n\n try {\n const data = await promise;\n // Store the response with a timestamp\n responseCache.set(cacheKey, { data, created: Date.now() });\n // Clear any previous failure\n failureCache.delete(cacheKey);\n return data;\n } catch (error) {\n // Record the failure timestamp\n failureCache.set(cacheKey, Date.now());\n throw error;\n } finally {\n // Clear the promise cache so subsequent calls can re-fetch after TTL\n promiseCache.delete(cacheKey);\n }\n}\n\n/**\n * Get a cache handle for a specific key, useful for manual invalidation.\n *\n * @example\n * ```ts\n * // Invalidate merchant info cache after a mutation\n * getCache(\"frak_getMerchantInformation\").clear();\n * ```\n */\nexport function getCache(cacheKey: string) {\n return {\n /** Clear both the pending promise and the cached response */\n clear: () => {\n const { promiseCache, responseCache } = caches();\n promiseCache.delete(cacheKey);\n responseCache.delete(cacheKey);\n },\n /** Check if a non-expired response exists */\n has: (cacheTime: number = DEFAULT_CACHE_TIME) => {\n const { responseCache } = caches();\n const cached = responseCache.get(cacheKey);\n if (!cached) return false;\n return Date.now() - cached.created < cacheTime;\n },\n };\n}\n\n/**\n * Clear all cached data (both pending promises and resolved responses).\n * Called automatically when the client is destroyed.\n */\nexport function clearAllCache() {\n if (!store) return;\n store.promiseCache.clear();\n store.responseCache.clear();\n store.failureCache.clear();\n}\n","/**\n * Default production backend URL\n */\nconst DEFAULT_BACKEND_URL = \"https://backend.frak.id\";\n\n/**\n * Check if wallet URL is local development (port 3000 or 3010)\n */\nfunction isLocalDevelopment(walletUrl: string): boolean {\n return (\n walletUrl.includes(\"localhost:3000\") ||\n walletUrl.includes(\"localhost:3010\")\n );\n}\n\n/**\n * Derive backend URL from wallet URL\n * Maps wallet URLs to their corresponding backend URLs\n */\nfunction deriveBackendUrl(walletUrl: string): string {\n if (isLocalDevelopment(walletUrl)) {\n return \"https://localhost:3030\";\n }\n // Dev environment\n if (\n walletUrl.includes(\"wallet-dev.frak.id\") ||\n walletUrl.includes(\"wallet.gcp-dev.frak.id\")\n ) {\n return \"https://backend.gcp-dev.frak.id\";\n }\n // Production\n return DEFAULT_BACKEND_URL;\n}\n\n/**\n * Get the backend URL for API calls\n * Tries to derive from SDK config, falls back to production\n *\n * @param walletUrl - Optional wallet URL to derive from (overrides global config)\n */\nexport function getBackendUrl(walletUrl?: string): string {\n // If explicit walletUrl provided, derive from it\n if (walletUrl) {\n return deriveBackendUrl(walletUrl);\n }\n\n // Try to get from global FrakSetup config\n if (typeof window !== \"undefined\") {\n const configWalletUrl = (\n window as {\n FrakSetup?: { client?: { config?: { walletUrl?: string } } };\n }\n ).FrakSetup?.client?.config?.walletUrl;\n\n if (configWalletUrl) {\n return deriveBackendUrl(configWalletUrl);\n }\n }\n\n // Fallback to production\n return DEFAULT_BACKEND_URL;\n}\n","/**\n * SDK config store — reactive singleton for the resolved merchant config.\n *\n * State lives directly on `window.__frakSdkConfig`.\n * Reactivity is handled via the `frak:config` CustomEvent on `window`.\n * Resolved configs are cached in localStorage (30 s TTL, stale-while-revalidate).\n *\n * Backend fetch responses are cached and deduplicated via `withCache`.\n * Also owns the `frak-merchant-id` sessionStorage compatibility key.\n */\n\nimport type { Language } from \"../types/config\";\nimport type {\n MerchantConfigResponse,\n SdkResolvedConfig,\n} from \"../types/resolvedConfig\";\nimport { clearAllCache, withCache } from \"../utils/cache\";\nimport { getBackendUrl } from \"./backendUrl\";\n\nconst GLOBAL_KEY = \"__frakSdkConfig\";\nconst CACHE_TTL = 30_000; // 30 seconds\nconst DEFAULT_CACHE_KEY = \"frak-config-cache\";\nconst MERCHANT_ID_KEY = \"frak-merchant-id\";\n\nconst cacheState = { key: DEFAULT_CACHE_KEY };\n\nconst isBrowser = typeof window !== \"undefined\";\n\ntype CacheEntry = { config: SdkResolvedConfig; timestamp: number };\n\ndeclare global {\n interface Window {\n [GLOBAL_KEY]?: SdkResolvedConfig;\n }\n interface WindowEventMap {\n \"frak:config\": CustomEvent<SdkResolvedConfig>;\n }\n}\n\nfunction freshEmptyConfig(): SdkResolvedConfig {\n return { isResolved: false, merchantId: \"\" };\n}\n\n// ---------------------------------------------------------------------------\n// localStorage cache (with in-memory parsed copy)\n// ---------------------------------------------------------------------------\n\nlet memoryEntry: CacheEntry | null = null;\n\nfunction loadCacheEntry(): CacheEntry | null {\n if (!isBrowser) return null;\n try {\n const raw = localStorage.getItem(cacheState.key);\n if (!raw) return null;\n const entry: CacheEntry = JSON.parse(raw);\n if (!entry.config?.isResolved) return null;\n memoryEntry = entry;\n return entry;\n } catch {\n return null;\n }\n}\n\nfunction readCache(): SdkResolvedConfig | undefined {\n return (memoryEntry ?? loadCacheEntry())?.config;\n}\n\nfunction isCacheFresh(): boolean {\n const entry = memoryEntry ?? loadCacheEntry();\n if (!entry) return false;\n return Date.now() - entry.timestamp < CACHE_TTL;\n}\n\nfunction writeCache(config: SdkResolvedConfig): void {\n if (!isBrowser || !config.isResolved) return;\n try {\n const entry: CacheEntry = { config, timestamp: Date.now() };\n localStorage.setItem(cacheState.key, JSON.stringify(entry));\n memoryEntry = entry;\n } catch {}\n}\n\nfunction removeCache(): void {\n if (!isBrowser) return;\n memoryEntry = null;\n try {\n localStorage.removeItem(cacheState.key);\n } catch {}\n}\n\n// ---------------------------------------------------------------------------\n// Initialise window-backed config (lazily, on first access)\n// ---------------------------------------------------------------------------\n\n// Seed `window.__frakSdkConfig` from the localStorage cache. Do NOT call this at\n// module top-level: that side effect contradicts `sideEffects: false` (a bundler\n// may legally drop it) and forces a localStorage read on every consumer at import.\n// It is invoked lazily from `getConfig` instead.\nfunction initConfig(): void {\n if (!isBrowser) return;\n if (window[GLOBAL_KEY]) return;\n window[GLOBAL_KEY] = readCache() ?? freshEmptyConfig();\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction getConfig(): SdkResolvedConfig {\n if (!isBrowser) return freshEmptyConfig();\n initConfig();\n return window[GLOBAL_KEY] ?? freshEmptyConfig();\n}\n\nfunction dispatch(config: SdkResolvedConfig): void {\n if (!isBrowser) return;\n window.dispatchEvent(new CustomEvent(\"frak:config\", { detail: config }));\n}\n\nfunction getTargetDomain(domain?: string): string {\n return domain ?? (isBrowser ? window.location.hostname : \"\");\n}\n\n// ---------------------------------------------------------------------------\n// Merchant config fetching (resolve)\n// ---------------------------------------------------------------------------\n\nasync function fetchFromBackend(\n targetDomain: string,\n walletUrl?: string,\n lang?: Language\n): Promise<MerchantConfigResponse | undefined> {\n try {\n const backendUrl = getBackendUrl(walletUrl);\n const langParam = lang ? `&lang=${encodeURIComponent(lang)}` : \"\";\n const response = await fetch(\n `${backendUrl}/user/merchant/resolve?domain=${encodeURIComponent(targetDomain)}${langParam}`\n );\n\n if (!response.ok) {\n console.warn(\n `[Frak SDK] Merchant lookup failed for domain ${targetDomain}: ${response.status}`\n );\n return undefined;\n }\n\n const data = (await response.json()) as MerchantConfigResponse;\n\n // Write compatibility sessionStorage key\n if (isBrowser) {\n try {\n sessionStorage.setItem(MERCHANT_ID_KEY, data.merchantId);\n } catch {}\n }\n\n return data;\n } catch (error) {\n console.warn(\"[Frak SDK] Failed to fetch merchant config:\", error);\n return undefined;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport const sdkConfigStore = {\n getConfig,\n\n get isResolved(): boolean {\n return getConfig().isResolved;\n },\n\n get isCacheFresh(): boolean {\n return isCacheFresh();\n },\n\n setCacheScope(domain: string, lang?: string): void {\n const suffix = `${domain}:${lang ?? \"\"}`;\n cacheState.key = `${DEFAULT_CACHE_KEY}:${suffix}`;\n memoryEntry = null;\n },\n\n setConfig(config: SdkResolvedConfig): void {\n if (isBrowser) window[GLOBAL_KEY] = config;\n writeCache(config);\n dispatch(config);\n\n // Keep sessionStorage merchantId in sync\n if (isBrowser && config.merchantId) {\n try {\n sessionStorage.setItem(MERCHANT_ID_KEY, config.merchantId);\n } catch {}\n }\n },\n\n reset(): void {\n const next = readCache() ?? freshEmptyConfig();\n if (isBrowser) window[GLOBAL_KEY] = next;\n dispatch(next);\n },\n\n clearCache(): void {\n removeCache();\n clearAllCache();\n if (isBrowser) {\n try {\n sessionStorage.removeItem(MERCHANT_ID_KEY);\n } catch {}\n }\n },\n\n resolve(\n domain?: string,\n walletUrl?: string,\n lang?: Language\n ): Promise<MerchantConfigResponse | undefined> {\n const targetDomain = getTargetDomain(domain);\n if (!targetDomain) {\n return Promise.resolve(undefined);\n }\n\n const cacheKey = `sdkConfig:${targetDomain}:${lang ?? \"\"}`;\n\n return withCache(\n async () => {\n const result = await fetchFromBackend(\n targetDomain,\n walletUrl,\n lang\n );\n // Throw on failure so withCache doesn't cache undefined\n if (!result) {\n throw new Error(\"Config resolution returned empty\");\n }\n return result;\n },\n { cacheKey, cacheTime: Number.POSITIVE_INFINITY }\n ).catch(() => undefined);\n },\n\n getMerchantId(): string | undefined {\n const config = getConfig();\n if (config.isResolved && config.merchantId) {\n return config.merchantId;\n }\n\n if (isBrowser) {\n try {\n return sessionStorage.getItem(MERCHANT_ID_KEY) ?? undefined;\n } catch {}\n }\n return undefined;\n },\n\n async resolveMerchantId(\n domain?: string,\n walletUrl?: string\n ): Promise<string | undefined> {\n const fast = sdkConfigStore.getMerchantId();\n if (fast) return fast;\n\n const config = await sdkConfigStore.resolve(domain, walletUrl);\n return config?.merchantId;\n },\n};\n","/**\n * Encode a buffer to a base64url encoded string\n * @param buffer The buffer to encode\n * @returns The encoded string\n */\nexport function base64urlEncode(buffer: Uint8Array): string {\n return btoa(Array.from(buffer, (b) => String.fromCharCode(b)).join(\"\"))\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\n/**\n * Decode a base64url encoded string\n * @param value The value to decode\n * @returns The decoded value\n */\nexport function base64urlDecode(value: string): Uint8Array {\n const m = value.length % 4;\n return Uint8Array.from(\n atob(\n value\n .replace(/-/g, \"+\")\n .replace(/_/g, \"/\")\n .padEnd(value.length + (m === 0 ? 0 : 4 - m), \"=\")\n ),\n (c) => c.charCodeAt(0)\n );\n}\n","import { jsonEncode } from \"@frak-labs/frame-connector\";\nimport { base64urlEncode } from \"./b64\";\n\n/**\n * Compress json data\n * @param data\n * @ignore\n */\nexport function compressJsonToB64(data: unknown): string {\n return base64urlEncode(jsonEncode(data));\n}\n","import type { Hex } from \"viem\";\nimport type { PrepareSsoParamsType, SsoMetadata } from \"../../types\";\nimport { compressJsonToB64 } from \"../compression/compress\";\n\nexport type AppSpecificSsoMetadata = SsoMetadata & {\n name?: string;\n css?: string;\n};\n\n/**\n * The full SSO params that will be used for compression\n */\nexport type FullSsoParams = Omit<PrepareSsoParamsType, \"metadata\"> & {\n metadata: AppSpecificSsoMetadata;\n merchantId: string;\n clientId: string;\n};\n\n/**\n * Generate SSO URL with compressed parameters\n * This mirrors the wallet's getOpenSsoLink() function\n *\n * @param walletUrl - Base wallet URL (e.g., \"https://wallet.frak.id\")\n * @param params - SSO parameters\n * @param merchantId - Merchant identifier\n * @param name - Application name\n * @param clientId - Client identifier for identity tracking\n * @param css - Optional custom CSS\n * @returns Complete SSO URL ready to open in popup or redirect\n *\n * @example\n * ```ts\n * const ssoUrl = generateSsoUrl(\n * \"https://wallet.frak.id\",\n * { metadata: { logoUrl: \"...\" }, directExit: true },\n * \"0x123...\",\n * \"My App\"\n * );\n * // Returns: https://wallet.frak.id/sso?p=<compressed_base64>\n * ```\n */\nexport function generateSsoUrl(\n walletUrl: string,\n params: PrepareSsoParamsType,\n merchantId: string,\n name: string | undefined,\n clientId: string,\n css?: string\n): string {\n // Build full params with app-specific metadata\n const fullParams: FullSsoParams = {\n redirectUrl: params.redirectUrl,\n directExit: params.directExit,\n lang: params.lang,\n merchantId,\n metadata: {\n name,\n css,\n logoUrl: params.metadata?.logoUrl,\n homepageLink: params.metadata?.homepageLink,\n },\n clientId,\n };\n\n // Compress params to minimal format\n const compressedParam = ssoParamsToCompressed(fullParams);\n\n // Encode to base64url\n const compressedString = compressJsonToB64(compressedParam);\n\n // Build URL matching wallet's expected format: /sso?p=<compressed>\n const ssoUrl = new URL(walletUrl);\n ssoUrl.pathname = \"/sso\";\n ssoUrl.searchParams.set(\"p\", compressedString);\n\n return ssoUrl.toString();\n}\n\n/**\n * Map full sso params to compressed sso params\n * @param params\n */\nfunction ssoParamsToCompressed(params: FullSsoParams): CompressedSsoData {\n return {\n r: params.redirectUrl,\n cId: params.clientId,\n d: params.directExit,\n l: params.lang,\n m: params.merchantId,\n md: {\n n: params.metadata?.name,\n css: params.metadata?.css,\n l: params.metadata?.logoUrl,\n h: params.metadata?.homepageLink,\n },\n };\n}\n\n/**\n * Type of compressed the sso data\n */\nexport type CompressedSsoData = {\n // Potential id from backend\n id?: Hex;\n // Client id\n cId: string;\n // redirect url\n r?: string;\n // direct exit\n d?: boolean;\n // language\n l?: \"en\" | \"fr\";\n // merchant id\n m: string;\n // metadata\n md: {\n n?: string;\n css?: string;\n l?: string;\n h?: string;\n };\n};\n","import { getClientId } from \"../config/clientId\";\nimport { sdkConfigStore } from \"../config/sdkConfigStore\";\nimport type {\n FrakClient,\n OpenSsoParamsType,\n OpenSsoReturnType,\n} from \"../types\";\nimport { generateSsoUrl } from \"../utils/sso/sso\";\n\n// SSO popup configuration\nexport const ssoPopupFeatures =\n \"menubar=no,status=no,scrollbars=no,fullscreen=no,width=500, height=800\";\nexport const ssoPopupName = \"frak-sso\";\n\n/**\n * Function used to open the SSO\n * @param client - The current Frak Client\n * @param args - The SSO parameters\n *\n * @description Two SSO flow modes:\n *\n * **Redirect Mode** (openInSameWindow: true):\n * - Wallet generates URL and triggers redirect\n * - Used when redirectUrl is provided\n *\n * **Popup Mode** (openInSameWindow: false/omitted):\n * - SDK generates URL client-side (or uses provided ssoPopupUrl)\n * - Opens popup synchronously (prevents popup blockers)\n * - Waits for SSO completion via postMessage\n *\n * @example\n * First we build the sso metadata\n * ```ts\n * // Build the metadata\n * const metadata: SsoMetadata = {\n * logoUrl: \"https://my-app.com/logo.png\",\n * homepageLink: \"https://my-app.com\",\n * };\n * ```\n *\n * Then, either use it with direct exit (and so user is directly redirected to your website), or a custom redirect URL\n * :::code-group\n * ```ts [Popup (default)]\n * // Opens in popup, SDK generates URL automatically\n * await openSso(frakConfig, {\n * directExit: true,\n * metadata,\n * });\n * ```\n * ```ts [Redirect]\n * // Opens in same window with redirect\n * await openSso(frakConfig, {\n * redirectUrl: \"https://my-app.com/frak-sso\",\n * metadata,\n * openInSameWindow: true,\n * });\n * ```\n * ```ts [Custom popup URL]\n * // Advanced: provide custom SSO URL\n * const { ssoUrl } = await prepareSso(frakConfig, { metadata });\n * await openSso(frakConfig, {\n * metadata,\n * ssoPopupUrl: `${ssoUrl}&custom=param`,\n * });\n * ```\n * :::\n */\nexport async function openSso(\n client: FrakClient,\n inputArgs: OpenSsoParamsType\n): Promise<OpenSsoReturnType> {\n const { metadata, customizations, walletUrl } = client.config;\n\n // Apply default: when no redirectUrl is provided we want the SSO popup\n // to close itself after completion. Without this default the popup\n // sticks on the success screen and the \"Redirect now\" button is a no-op.\n const args: OpenSsoParamsType = {\n ...inputArgs,\n directExit: inputArgs.directExit ?? !inputArgs.redirectUrl,\n };\n\n // Check if redirect mode (default to true if redirectUrl present)\n const isRedirectMode = args.openInSameWindow ?? !!args.redirectUrl;\n if (isRedirectMode) {\n // Redirect flow: Wallet generates URL and triggers redirect via lifecycle event\n // This must happen on wallet side because only the iframe can trigger the redirect\n return await client.request({\n method: \"frak_openSso\",\n params: [args, metadata.name, customizations?.css],\n });\n }\n\n // Popup flow: Generate URL on SDK side and open synchronously\n // This ensures window.open() is called in same tick as user gesture (no popup blocker)\n\n // Step 1: Generate or use provided SSO URL\n const ssoUrl =\n args.ssoPopupUrl ??\n generateSsoUrl(\n walletUrl ?? \"https://wallet.frak.id\",\n args,\n (await sdkConfigStore.resolveMerchantId()) ?? \"\",\n metadata.name,\n getClientId(),\n customizations?.css\n );\n\n // Step 2: Open popup synchronously (critical for popup blocker prevention)\n const popup = window.open(ssoUrl, ssoPopupName, ssoPopupFeatures);\n if (!popup) {\n throw new Error(\n \"Popup was blocked. Please allow popups for this site.\"\n );\n }\n popup.focus();\n\n // Step 3: Wait for SSO completion via RPC\n // The wallet iframe will resolve this when SSO page sends sso_complete message\n const result = await client.request({\n method: \"frak_openSso\",\n params: [args, metadata.name, customizations?.css],\n });\n\n return result ?? {};\n}\n","import type { FrakClient } from \"../../types\";\nimport type { SdkEventMap } from \"./events\";\n\n/**\n * Track an analytics event via the SDK's OpenPanel instance.\n * Fire-and-forget — silently catches errors so analytics never break a\n * partner integration.\n *\n * The client must be passed explicitly because the OpenPanel instance is\n * scoped to each `FrakClient` (a partner site may hold multiple iframes).\n *\n * @param client - The Frak client instance (no-op if undefined)\n * @param event - Typed event name from the SDK event map\n * @param properties - Typed properties for the given event\n */\nexport function trackEvent<K extends keyof SdkEventMap>(\n client: FrakClient | undefined,\n event: K,\n properties?: SdkEventMap[K]\n): void {\n if (!client) {\n console.debug(\"[Frak] No client provided, skipping event tracking\");\n return;\n }\n\n try {\n client.openPanel?.track(\n event as string,\n properties as Record<string, unknown> | undefined\n );\n } catch (e) {\n console.debug(\"[Frak] Failed to track event:\", event, e);\n }\n}\n","/**\n * Case-insensitive helpers for reading URL query parameters.\n *\n * Some email tools (Klaviyo, Omnisend, Customer.io …) and a few browsers\n * lowercase the entire URL before the recipient opens it, so a mixed-case key\n * authored as `frakAction` or `fCtx` can arrive as `frakaction` / `fctx`. A\n * plain `searchParams.get(\"frakAction\")` would miss it. Matching the key\n * ignoring case keeps UI-triggering and referral params working regardless of\n * how the link was mangled in transit.\n *\n * Note: only the key is normalised. An encoded value (base64url, tokens) is not\n * recoverable if the same channel also lowercased the value itself.\n */\n\n/**\n * Read a query parameter, matching its key case-insensitively.\n *\n * An exact-case match wins when present, so a canonical link is never shadowed\n * by a mangled duplicate (`?fctx=stale&fCtx=real` resolves to `real`). Only when\n * the exact key is absent do we scan for a case-folded variant.\n *\n * @returns the param value, or `null` when no key matches.\n */\nexport function getQueryParamCaseInsensitive(\n searchParams: URLSearchParams,\n key: string\n): string | null {\n const exact = searchParams.get(key);\n if (exact !== null) return exact;\n\n const target = key.toLowerCase();\n for (const [paramKey, value] of searchParams) {\n if (paramKey.toLowerCase() === target) {\n return value;\n }\n }\n return null;\n}\n\n/**\n * Delete every query parameter whose key matches `key` case-insensitively.\n *\n * Keys are collected before deletion because mutating a `URLSearchParams`\n * while iterating it skips entries.\n */\nexport function deleteQueryParamCaseInsensitive(\n searchParams: URLSearchParams,\n key: string\n): void {\n const target = key.toLowerCase();\n const matchingKeys = [...searchParams.keys()].filter(\n (paramKey) => paramKey.toLowerCase() === target\n );\n for (const paramKey of matchingKeys) {\n searchParams.delete(paramKey);\n }\n}\n","import type { Address } from \"viem\";\n\n/**\n * Address utilities — minimal, dependency-free replacements for the subset of\n * `viem` helpers we used to import. Keeping these in-house lets the SDK ship\n * without pulling viem's checksum/keccak/error chain into the bundle.\n *\n * Scope is intentionally narrow:\n * - `isAddress`: shape-only validation (no EIP-55 checksum)\n * - `areAddressesEqual`: case-insensitive equality\n * - `addressToBytes` / `bytesToAddress`: fixed 20-byte conversion\n */\n\n/** Matches a 0x-prefixed 40-char hex string regardless of case. */\nconst ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/;\n\n/**\n * Check whether a value is a syntactically valid Ethereum address.\n *\n * This intentionally skips EIP-55 checksum validation: the SDK never produces\n * checksum-cased payloads, and downstream consumers (wallet, indexer) treat\n * addresses case-insensitively. Avoiding the checksum path drops keccak256 +\n * @noble/hashes from the bundle.\n */\nexport function isAddress(value: unknown): value is Address {\n return typeof value === \"string\" && ADDRESS_REGEX.test(value);\n}\n\n/**\n * Case-insensitive equality check for two Ethereum addresses.\n *\n * Both inputs are assumed to be syntactically valid addresses; callers that\n * receive untrusted input should validate via {@link isAddress} first.\n */\nexport function areAddressesEqual(a: Address, b: Address): boolean {\n return a.toLowerCase() === b.toLowerCase();\n}\n\n/**\n * Decode a 20-byte Ethereum address into a fixed-size Uint8Array(20).\n *\n * Throws when the input is not exactly `0x` + 40 hex chars — callers wrap\n * the call in try/catch (see {@link FrakContextManager.compress}) so any\n * malformed input degrades to a graceful undefined return.\n */\nexport function addressToBytes(address: Address): Uint8Array {\n const bytes = new Uint8Array(20);\n for (let i = 0; i < 20; i++) {\n const byte = Number.parseInt(\n address.substring(2 + i * 2, 4 + i * 2),\n 16\n );\n if (Number.isNaN(byte)) {\n throw new Error(`Invalid address: ${address}`);\n }\n bytes[i] = byte;\n }\n return bytes;\n}\n\n/** Lookup table avoids `padStart` overhead in the hot encode loop. */\nconst HEX_BYTE = /*#__PURE__*/ Array.from({ length: 256 }, (_, i) =>\n i.toString(16).padStart(2, \"0\")\n);\n\n/**\n * Encode a 20-byte Uint8Array (or a 20-byte subarray view) into a lowercase\n * hex Ethereum address. The caller MUST guarantee `bytes.length === 20`.\n */\nexport function bytesToAddress(bytes: Uint8Array): Address {\n let out = \"0x\";\n for (let i = 0; i < 20; i++) {\n out += HEX_BYTE[bytes[i]];\n }\n return out as Address;\n}\n","import type { Address } from \"viem\";\n\n/**\n * V1 (legacy) Frak Context — contains only the referrer wallet address.\n * Used for backward compatibility with old sharing links.\n * @ignore\n */\nexport type FrakContextV1 = {\n /** Referrer wallet address */\n r: Address;\n};\n\n/**\n * V2 Frak Context — anonymous-first referral context with optional wallet.\n *\n * Carries merchant context (`m`) and creation timestamp (`t`) unconditionally.\n * Identifies the sharer via either the anonymous clientId (`c`) or, when the\n * sharer is authenticated, the stronger wallet identifier (`w`). A valid V2\n * context MUST contain at least one of `c` or `w`; both may be present when\n * a logged-in user shares a link (best attribution signal).\n *\n * `w` takes precedence as the source of truth because the wallet is bound to\n * the user's WebAuthn credential, survives localStorage clears, and is global\n * across merchants — unlike `c`, which is a per-browser UUID.\n *\n * @ignore\n */\nexport type FrakContextV2 = {\n /** Version discriminator */\n v: 2;\n /** Merchant ID (UUID) */\n m: string;\n /** Link creation timestamp (epoch seconds) */\n t: number;\n /** Sharer's anonymous clientId (UUID from localStorage). Optional when `w` is provided. */\n c?: string;\n /** Sharer's wallet address. Preferred source of truth when the sharer is authenticated. Optional when `c` is provided. */\n w?: Address;\n};\n\n/**\n * The current Frak Context — union of all versions.\n *\n * - No `v` field → V1 (legacy wallet address)\n * - `v: 2` → V2 (anonymous clientId-based)\n *\n * @ignore\n */\nexport type FrakContext = FrakContextV1 | FrakContextV2;\n\n/**\n * Type guard: check if a context is V1 (legacy wallet address).\n * @param ctx - The Frak context to check\n * @returns True if the context is a V1 context\n */\nexport function isV1Context(ctx: FrakContext): ctx is FrakContextV1 {\n return \"r\" in ctx && !(\"v\" in ctx);\n}\n\n/**\n * Type guard: check if a context is V2 (anonymous clientId-based).\n * @param ctx - The Frak context to check\n * @returns True if the context is a V2 context\n */\nexport function isV2Context(ctx: FrakContext): ctx is FrakContextV2 {\n return \"v\" in ctx && ctx.v === 2;\n}\n","/**\n * Binary codec for {@link FrakContextV2}.\n *\n * Produces a compact, URL-safe byte layout (~65% smaller than the previous\n * JSON+base64url format). See the layout below.\n *\n * ## Wire layout\n *\n * ```text\n * byte 0: header\n * bits 0-3 version (= 2)\n * bit 4 has_c flag\n * bit 5 has_w flag\n * bits 6-7 reserved (must be 0)\n * bytes 1..16: merchant UUID (16 bytes, mandatory)\n * bytes 17..20: timestamp (uint32 big-endian, Unix seconds)\n * bytes 21..36: client UUID (16 bytes, only when has_c is set)\n * bytes 37..56: wallet address (20 bytes, only when has_w is set)\n * ```\n *\n * Size variants (before base64url):\n * - has_c only: 37 bytes\n * - has_w only: 41 bytes\n * - has_c + has_w: 57 bytes\n *\n * V1 payloads are exactly 20 bytes (raw wallet address); the byte lengths never\n * overlap, so the outer decoder can disambiguate purely on length.\n *\n * @ignore\n */\nimport type { Address } from \"viem\";\nimport type { FrakContextV2 } from \"../types\";\nimport { addressToBytes, bytesToAddress, isAddress } from \"./address\";\n\nconst VERSION_V2 = 0x02;\nconst VERSION_MASK = 0x0f;\nconst FLAG_HAS_C = 1 << 4;\nconst FLAG_HAS_W = 1 << 5;\nconst RESERVED_MASK = 0xc0;\n\nconst UUID_BYTES = 16;\nconst TIMESTAMP_BYTES = 4;\nconst ADDRESS_BYTES = 20;\nconst HEADER_BYTES = 1;\n\nconst UUID_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Strict lower-case UUID validation (RFC 4122 shape, any version/variant). */\nfunction isUuid(value: unknown): value is string {\n return typeof value === \"string\" && UUID_RE.test(value);\n}\n\n/** Parse a canonical UUID string into 16 raw bytes. */\nfunction uuidToBytes(uuid: string): Uint8Array {\n const hex = uuid.replace(/-/g, \"\");\n const out = new Uint8Array(UUID_BYTES);\n for (let i = 0; i < UUID_BYTES; i++) {\n out[i] = Number.parseInt(hex.substring(i * 2, i * 2 + 2), 16);\n }\n return out;\n}\n\n/** Format 16 raw bytes as a canonical 8-4-4-4-12 UUID string. */\nfunction bytesToUuid(bytes: Uint8Array): string {\n let hex = \"\";\n for (let i = 0; i < UUID_BYTES; i++) {\n hex += bytes[i].toString(16).padStart(2, \"0\");\n }\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;\n}\n\n/**\n * Encode a {@link FrakContextV2} into its binary wire format.\n *\n * Returns `null` when the context fails runtime validation (missing fields,\n * malformed UUIDs, timestamp outside uint32 range, invalid wallet).\n */\nexport function encodeFrakContextV2(ctx: FrakContextV2): Uint8Array | null {\n if (!isUuid(ctx.m)) return null;\n if (!Number.isInteger(ctx.t) || ctx.t < 0 || ctx.t > 0xff_ff_ff_ff)\n return null;\n\n const hasC = typeof ctx.c === \"string\" && ctx.c.length > 0;\n const hasW = typeof ctx.w === \"string\" && isAddress(ctx.w);\n if (!hasC && !hasW) return null;\n if (hasC && !isUuid(ctx.c)) return null;\n\n const size =\n HEADER_BYTES +\n UUID_BYTES +\n TIMESTAMP_BYTES +\n (hasC ? UUID_BYTES : 0) +\n (hasW ? ADDRESS_BYTES : 0);\n\n const buf = new Uint8Array(size);\n const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);\n let offset = 0;\n\n buf[offset++] =\n VERSION_V2 | (hasC ? FLAG_HAS_C : 0) | (hasW ? FLAG_HAS_W : 0);\n\n buf.set(uuidToBytes(ctx.m), offset);\n offset += UUID_BYTES;\n\n view.setUint32(offset, ctx.t, false);\n offset += TIMESTAMP_BYTES;\n\n if (hasC) {\n buf.set(uuidToBytes(ctx.c as string), offset);\n offset += UUID_BYTES;\n }\n\n if (hasW) {\n buf.set(addressToBytes(ctx.w as Address), offset);\n offset += ADDRESS_BYTES;\n }\n\n return buf;\n}\n\n/**\n * Decode a binary {@link FrakContextV2} payload.\n *\n * Returns `null` when:\n * - the header version nibble is not V2\n * - reserved header bits are set (guards against future-version payloads)\n * - neither flag is set (invalid: V2 must carry `c` and/or `w`)\n * - the byte length does not match the length implied by the header flags\n * - the decoded wallet does not pass `isAddress` (defense-in-depth against\n * crafted payloads that round-trip by length but carry junk)\n */\nexport function decodeFrakContextV2(buf: Uint8Array): FrakContextV2 | null {\n if (buf.length < HEADER_BYTES + UUID_BYTES + TIMESTAMP_BYTES) return null;\n\n const header = buf[0];\n if ((header & VERSION_MASK) !== VERSION_V2) return null;\n if ((header & RESERVED_MASK) !== 0) return null;\n\n const hasC = (header & FLAG_HAS_C) !== 0;\n const hasW = (header & FLAG_HAS_W) !== 0;\n if (!hasC && !hasW) return null;\n\n const expected =\n HEADER_BYTES +\n UUID_BYTES +\n TIMESTAMP_BYTES +\n (hasC ? UUID_BYTES : 0) +\n (hasW ? ADDRESS_BYTES : 0);\n if (buf.length !== expected) return null;\n\n let offset = HEADER_BYTES;\n\n const m = bytesToUuid(buf.subarray(offset, offset + UUID_BYTES));\n offset += UUID_BYTES;\n\n const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);\n const t = view.getUint32(offset, false);\n offset += TIMESTAMP_BYTES;\n\n const out: FrakContextV2 = { v: 2, m, t };\n\n if (hasC) {\n out.c = bytesToUuid(buf.subarray(offset, offset + UUID_BYTES));\n offset += UUID_BYTES;\n }\n\n if (hasW) {\n const walletHex = bytesToAddress(\n buf.subarray(offset, offset + ADDRESS_BYTES)\n );\n if (!isAddress(walletHex)) return null;\n out.w = walletHex;\n offset += ADDRESS_BYTES;\n }\n\n return out;\n}\n\n/**\n * Quick length-based probe to tell V1 (20-byte wallet address) apart from a V2\n * binary payload. Exposed so the outer decoder can branch without re-parsing.\n */\nexport function isV2BinaryLength(byteLength: number): boolean {\n return (\n byteLength ===\n HEADER_BYTES + UUID_BYTES + TIMESTAMP_BYTES + UUID_BYTES ||\n byteLength ===\n HEADER_BYTES + UUID_BYTES + TIMESTAMP_BYTES + ADDRESS_BYTES ||\n byteLength ===\n HEADER_BYTES +\n UUID_BYTES +\n TIMESTAMP_BYTES +\n UUID_BYTES +\n ADDRESS_BYTES\n );\n}\n","import type {\n AttributionParams,\n FrakContext,\n FrakContextV1,\n FrakContextV2,\n} from \"../types\";\nimport { isV2Context } from \"../types\";\nimport { base64urlDecode, base64urlEncode } from \"../utils/compression/b64\";\nimport {\n deleteQueryParamCaseInsensitive,\n getQueryParamCaseInsensitive,\n} from \"../utils/url/queryParams\";\nimport { addressToBytes, bytesToAddress, isAddress } from \"./address\";\nimport { decodeFrakContextV2, encodeFrakContextV2 } from \"./frakContextV2Codec\";\n\n/**\n * URL parameter key for the Frak referral context\n */\nconst contextKey = \"fCtx\";\n\n/**\n * Compress a Frak context into a URL-safe string.\n *\n * - V2 contexts are encoded using a compact binary layout (see\n * {@link encodeFrakContextV2}) then base64url-encoded.\n * - V1 contexts encode the wallet address as raw bytes (base64url).\n *\n * @param context - The context to compress (V1 or V2)\n * @returns A compressed base64url string, or undefined on failure\n */\nfunction compress(context?: FrakContextV1 | FrakContextV2): string | undefined {\n if (!context) return;\n try {\n if (isV2Context(context)) {\n const encoded = encodeFrakContextV2(context);\n if (!encoded) return undefined;\n return base64urlEncode(encoded);\n }\n\n // V1 legacy: compress wallet address as raw bytes\n const bytes = addressToBytes(context.r);\n return base64urlEncode(bytes);\n } catch (e) {\n console.error(\"Error compressing Frak context\", { e, context });\n }\n return undefined;\n}\n\n/**\n * Decompress a base64url string back into a Frak context.\n *\n * V1 (exactly 20 bytes) and V2 (37, 41, or 57 bytes) are distinguished by\n * their decoded byte length, so there is no ambiguity.\n *\n * @param context - The compressed context string\n * @returns The decompressed FrakContext, or undefined on failure\n */\nfunction decompress(context?: string): FrakContext | undefined {\n if (!context || context.length === 0) return;\n try {\n const bytes = base64urlDecode(context);\n\n // V1 is a raw 20-byte wallet address; V2 binary is always longer\n // and starts with a header byte whose low nibble is the version.\n if (bytes.length !== 20) {\n const v2 = decodeFrakContextV2(bytes);\n if (v2) return v2;\n return undefined;\n }\n\n const hex = bytesToAddress(bytes);\n if (isAddress(hex)) {\n return { r: hex };\n }\n } catch (e) {\n console.error(\"Error decompressing Frak context\", { e, context });\n }\n return undefined;\n}\n\n/**\n * Parse a URL to extract the Frak referral context from the `fCtx` query parameter.\n *\n * The key is matched case-insensitively: some link channels (emails, messaging\n * apps) lowercase query-param keys in transit, so `fCtx` can arrive as `fctx`.\n *\n * @param args\n * @param args.url - The URL to parse\n * @returns The parsed FrakContext, or null if absent\n */\nfunction parse({ url }: { url: string }): FrakContext | null | undefined {\n if (!url) return null;\n\n const urlObj = new URL(url);\n const frakContext = getQueryParamCaseInsensitive(\n urlObj.searchParams,\n contextKey\n );\n if (!frakContext) return null;\n\n return decompress(frakContext);\n}\n\n/**\n * Default utm_source / via value when attribution is requested.\n */\nconst DEFAULT_ATTRIBUTION_SOURCE = \"frak\";\n\n/**\n * Resolve attribution defaults from the provided context.\n *\n * V2 contexts expose the merchantId (`m`) and, when anonymous, the clientId\n * (`c`), which feed `utm_campaign` and `ref` respectively. When V2 only carries\n * a wallet (`w`), `ref` is intentionally left unset — we don't want wallet\n * addresses leaking into UTM params. V1 contexts have no equivalent.\n */\nfunction resolveAttributionValues(\n overrides: AttributionParams\n): Record<string, string | undefined> {\n return {\n utm_source: overrides.utmSource ?? DEFAULT_ATTRIBUTION_SOURCE,\n utm_medium: overrides.utmMedium ?? undefined,\n utm_campaign: overrides.utmCampaign ?? undefined,\n utm_content: overrides.utmContent,\n utm_term: overrides.utmTerm,\n via: overrides.via ?? undefined,\n ref: overrides.ref ?? undefined,\n };\n}\n\n/**\n * Append attribution query params to a URL using gap-fill semantics.\n *\n * Existing params on the URL are preserved untouched (so merchant-provided\n * UTMs take precedence). Only missing keys are populated.\n */\nfunction applyAttributionParams(\n urlObj: URL,\n attribution?: AttributionParams\n): void {\n const values = resolveAttributionValues(attribution ?? {});\n for (const [key, value] of Object.entries(values)) {\n if (value === undefined || value === \"\") continue;\n if (urlObj.searchParams.has(key)) continue;\n urlObj.searchParams.set(key, value);\n }\n}\n\n/**\n * Add or replace the `fCtx` query parameter in a URL with the given context.\n *\n * Standard affiliation params (`utm_source`, `utm_medium`, `utm_campaign`,\n * `ref`, `via`, ...) are always appended using gap-fill semantics: pre-existing\n * params on the URL are preserved, defaults are derived from the context when\n * applicable, and `attribution` overrides take precedence when provided.\n *\n * @param args\n * @param args.url - The URL to update\n * @param args.context - The context to embed (V1 or V2)\n * @param args.attribution - Optional attribution overrides. Defaults are applied even when omitted.\n * @returns The updated URL string, or null on failure\n */\nfunction update({\n url,\n context,\n attribution,\n}: {\n url?: string;\n context: FrakContextV1 | FrakContextV2;\n attribution?: AttributionParams;\n}): string | null {\n if (!url) return null;\n\n const compressedContext = compress(context);\n if (!compressedContext) return null;\n\n const urlObj = new URL(url);\n deleteQueryParamCaseInsensitive(urlObj.searchParams, contextKey);\n urlObj.searchParams.set(contextKey, compressedContext);\n applyAttributionParams(urlObj, attribution);\n return urlObj.toString();\n}\n\n/**\n * Remove the `fCtx` query parameter from a URL.\n *\n * @param url - The URL to strip the context from\n * @returns The cleaned URL string\n */\nfunction remove(url: string): string {\n const urlObj = new URL(url);\n deleteQueryParamCaseInsensitive(urlObj.searchParams, contextKey);\n return urlObj.toString();\n}\n\n/**\n * Replace the current browser URL with an updated Frak context.\n *\n * - If `context` is non-null, embeds it via {@link update}.\n * - If `context` is null, strips the context via {@link remove}.\n *\n * @param args\n * @param args.url - Base URL (defaults to `window.location.href`)\n * @param args.context - Context to set, or null to remove\n */\nfunction replaceUrl({\n url: baseUrl,\n context,\n}: {\n url?: string;\n context: FrakContextV1 | FrakContextV2 | null;\n}) {\n if (!window.location?.href || typeof window === \"undefined\") {\n console.error(\"No window found, can't update context\");\n return;\n }\n\n const url = baseUrl ?? window.location.href;\n\n let newUrl: string | null;\n if (context !== null) {\n newUrl = update({ url, context });\n } else {\n newUrl = remove(url);\n }\n\n if (!newUrl) return;\n\n window.history.replaceState(null, \"\", newUrl.toString());\n}\n\n/**\n * Manager for Frak referral context in URLs.\n *\n * Handles compression, decompression, URL parsing, and browser history updates\n * for both V1 (wallet address) and V2 (anonymous clientId) referral contexts.\n */\nexport const FrakContextManager = {\n compress,\n decompress,\n parse,\n update,\n remove,\n replaceUrl,\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e={eur:`fr-FR`,usd:`en-US`,gbp:`en-GB`};function t(t){return t&&t in e?t:`eur`}function n(t){return t?e[t]??e.eur:e.eur}function r(e,r){let i=n(r),a=t(r);return e.toLocaleString(i,{style:`currency`,currency:a,minimumFractionDigits:0,maximumFractionDigits:2})}function i(e){return e?`${e}Amount`:`eurAmount`}export{r as n,t as r,i as t};
|
|
2
|
+
//# sourceMappingURL=getCurrencyAmountKey-DQcwjJiB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getCurrencyAmountKey-DQcwjJiB.js","names":[],"sources":["../src/constants/locales.ts","../src/utils/format/getSupportedCurrency.ts","../src/utils/format/getSupportedLocale.ts","../src/utils/format/formatAmount.ts","../src/utils/format/getCurrencyAmountKey.ts"],"sourcesContent":["/**\n * The keys for each locales\n * @inline\n */\nexport type LocalesKey = keyof typeof locales;\n\n/**\n * Map the currency to the locale\n */\nexport const locales = {\n eur: \"fr-FR\",\n usd: \"en-US\",\n gbp: \"en-GB\",\n} as const;\n","import { locales } from \"../../constants/locales\";\nimport type { Currency } from \"../../types\";\n\n/**\n * Get the supported currency for a given currency\n * @param currency - The currency to use\n * @returns The supported currency\n */\nexport function getSupportedCurrency(currency?: Currency): Currency {\n if (!currency) {\n return \"eur\";\n }\n return currency in locales ? currency : \"eur\";\n}\n","import { type LocalesKey, locales } from \"../../constants/locales\";\nimport type { Currency } from \"../../types\";\n\n/**\n * Get the supported locale for a given currency\n * @param currency - The currency to use\n * @returns The supported locale\n */\nexport function getSupportedLocale(\n currency?: Currency\n): (typeof locales)[LocalesKey] {\n if (!currency) {\n return locales.eur;\n }\n return locales[currency] ?? locales.eur;\n}\n","import type { Currency } from \"../../types\";\nimport { getSupportedCurrency } from \"./getSupportedCurrency\";\nimport { getSupportedLocale } from \"./getSupportedLocale\";\n\n/**\n * Format a numeric amount as a localized currency string\n * @param amount - The raw numeric amount to format\n * @param currency - Optional currency config; defaults to EUR/fr-FR when omitted\n * @returns Localized currency string (e.g. \"1 500 €\", \"$1,500\")\n */\nexport function formatAmount(amount: number, currency?: Currency) {\n // Get the supported locale (e.g. \"fr-FR\")\n const supportedLocale = getSupportedLocale(currency);\n\n // Get the supported currency (e.g. \"eur\")\n const supportedCurrency = getSupportedCurrency(currency);\n\n return amount.toLocaleString(supportedLocale, {\n style: \"currency\",\n currency: supportedCurrency,\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n });\n}\n","import type { Currency, TokenAmountType } from \"../../types\";\n\n/**\n * Get the currency amount key for a given currency\n * @param currency - The currency to use\n * @returns The currency amount key\n */\nexport function getCurrencyAmountKey(\n currency?: Currency\n): keyof TokenAmountType {\n if (!currency) {\n return \"eurAmount\";\n }\n return `${currency}Amount` as keyof TokenAmountType;\n}\n"],"mappings":"AASA,MAAa,EAAU,CACnB,IAAK,QACL,IAAK,QACL,IAAK,OACT,ECLA,SAAgB,EAAqB,EAA+B,CAIhE,OAHK,GAGE,KAAY,EAAU,EAAW,KAC5C,CCLA,SAAgB,EACZ,EAC4B,CAI5B,OAHK,EAGE,EAAQ,IAAa,EAAQ,IAFzB,EAAQ,GAGvB,CCLA,SAAgB,EAAa,EAAgB,EAAqB,CAE9D,IAAM,EAAkB,EAAmB,CAAQ,EAG7C,EAAoB,EAAqB,CAAQ,EAEvD,OAAO,EAAO,eAAe,EAAiB,CAC1C,MAAO,WACP,SAAU,EACV,sBAAuB,EACvB,sBAAuB,CAC3B,CAAC,CACL,CChBA,SAAgB,EACZ,EACqB,CAIrB,OAHK,EAGE,GAAG,EAAS,QAFR,WAGf"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e={eur:`fr-FR`,usd:`en-US`,gbp:`en-GB`};function t(t){return t&&t in e?t:`eur`}function n(t){return t?e[t]??e.eur:e.eur}function r(e,r){let i=n(r),a=t(r);return e.toLocaleString(i,{style:`currency`,currency:a,minimumFractionDigits:0,maximumFractionDigits:2})}function i(e){return e?`${e}Amount`:`eurAmount`}Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return t}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return i}});
|
|
2
|
+
//# sourceMappingURL=getCurrencyAmountKey-h2eOiYzv.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getCurrencyAmountKey-h2eOiYzv.cjs","names":[],"sources":["../src/constants/locales.ts","../src/utils/format/getSupportedCurrency.ts","../src/utils/format/getSupportedLocale.ts","../src/utils/format/formatAmount.ts","../src/utils/format/getCurrencyAmountKey.ts"],"sourcesContent":["/**\n * The keys for each locales\n * @inline\n */\nexport type LocalesKey = keyof typeof locales;\n\n/**\n * Map the currency to the locale\n */\nexport const locales = {\n eur: \"fr-FR\",\n usd: \"en-US\",\n gbp: \"en-GB\",\n} as const;\n","import { locales } from \"../../constants/locales\";\nimport type { Currency } from \"../../types\";\n\n/**\n * Get the supported currency for a given currency\n * @param currency - The currency to use\n * @returns The supported currency\n */\nexport function getSupportedCurrency(currency?: Currency): Currency {\n if (!currency) {\n return \"eur\";\n }\n return currency in locales ? currency : \"eur\";\n}\n","import { type LocalesKey, locales } from \"../../constants/locales\";\nimport type { Currency } from \"../../types\";\n\n/**\n * Get the supported locale for a given currency\n * @param currency - The currency to use\n * @returns The supported locale\n */\nexport function getSupportedLocale(\n currency?: Currency\n): (typeof locales)[LocalesKey] {\n if (!currency) {\n return locales.eur;\n }\n return locales[currency] ?? locales.eur;\n}\n","import type { Currency } from \"../../types\";\nimport { getSupportedCurrency } from \"./getSupportedCurrency\";\nimport { getSupportedLocale } from \"./getSupportedLocale\";\n\n/**\n * Format a numeric amount as a localized currency string\n * @param amount - The raw numeric amount to format\n * @param currency - Optional currency config; defaults to EUR/fr-FR when omitted\n * @returns Localized currency string (e.g. \"1 500 €\", \"$1,500\")\n */\nexport function formatAmount(amount: number, currency?: Currency) {\n // Get the supported locale (e.g. \"fr-FR\")\n const supportedLocale = getSupportedLocale(currency);\n\n // Get the supported currency (e.g. \"eur\")\n const supportedCurrency = getSupportedCurrency(currency);\n\n return amount.toLocaleString(supportedLocale, {\n style: \"currency\",\n currency: supportedCurrency,\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n });\n}\n","import type { Currency, TokenAmountType } from \"../../types\";\n\n/**\n * Get the currency amount key for a given currency\n * @param currency - The currency to use\n * @returns The currency amount key\n */\nexport function getCurrencyAmountKey(\n currency?: Currency\n): keyof TokenAmountType {\n if (!currency) {\n return \"eurAmount\";\n }\n return `${currency}Amount` as keyof TokenAmountType;\n}\n"],"mappings":"AASA,MAAa,EAAU,CACnB,IAAK,QACL,IAAK,QACL,IAAK,OACT,ECLA,SAAgB,EAAqB,EAA+B,CAIhE,OAHK,GAGE,KAAY,EAAU,EAAW,KAC5C,CCLA,SAAgB,EACZ,EAC4B,CAI5B,OAHK,EAGE,EAAQ,IAAa,EAAQ,IAFzB,EAAQ,GAGvB,CCLA,SAAgB,EAAa,EAAgB,EAAqB,CAE9D,IAAM,EAAkB,EAAmB,CAAQ,EAG7C,EAAoB,EAAqB,CAAQ,EAEvD,OAAO,EAAO,eAAe,EAAiB,CAC1C,MAAO,WACP,SAAU,EACV,sBAAuB,EACvB,sBAAuB,CAC3B,CAAC,CACL,CChBA,SAAgB,EACZ,EACqB,CAIrB,OAHK,EAGE,GAAG,EAAS,QAFR,WAGf"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as GetMerchantInformationReturnType } from "./merchantInformation-BSuahpjl.cjs";
|
|
2
|
+
import { B as FinalActionType, C as DisplayModalParamsType, D as ModalStepTypes, E as ModalRpcStepsResultType, L as PrepareSsoParamsType, M as SiweAuthenticateReturnType, N as SiweAuthenticationParams, O as SendTransactionModalStepType, P as LoginModalStepType, R as PrepareSsoReturnType, V as FinalModalStepType, b as DisplaySharingPageParamsType, d as WalletStatusReturnType, f as UserReferralStatusType, h as DisplayEmbeddedWalletResultType, i as FrakContext, k as SendTransactionReturnType, m as DisplayEmbeddedWalletParamsType, p as SendInteractionParamsType, s as FrakClient, w as ModalRpcMetadata, x as DisplaySharingPageResultType } from "./openSso-CKvkAGts.cjs";
|
|
2
3
|
|
|
3
4
|
//#region src/actions/displayEmbeddedWallet.d.ts
|
|
4
5
|
/**
|
|
@@ -643,4 +644,5 @@ declare function siweAuthenticate(client: FrakClient, {
|
|
|
643
644
|
metadata
|
|
644
645
|
}: SiweAuthenticateModalParams): Promise<SiweAuthenticateReturnType>;
|
|
645
646
|
//#endregion
|
|
646
|
-
export { displayEmbeddedWallet as C, displayModal as S, getUserReferralStatus as _, ModalBuilder as a, ensureIdentity as b, watchWalletStatus as c, REFERRAL_SUCCESS_EVENT as d, setupReferral as f, prepareSso as g, processReferral as h, sendTransaction as i, trackPurchaseStatus as l, ProcessReferralOptions as m, siweAuthenticate as n, ModalStepBuilder as o, referralInteraction as p, SendTransactionParams as r, modalBuilder as s, SiweAuthenticateModalParams as t, sendInteraction as u, getMergeToken as v, displaySharingPage as x, getMerchantInformation as y };
|
|
647
|
+
export { displayEmbeddedWallet as C, displayModal as S, getUserReferralStatus as _, ModalBuilder as a, ensureIdentity as b, watchWalletStatus as c, REFERRAL_SUCCESS_EVENT as d, setupReferral as f, prepareSso as g, processReferral as h, sendTransaction as i, trackPurchaseStatus as l, ProcessReferralOptions as m, siweAuthenticate as n, ModalStepBuilder as o, referralInteraction as p, SendTransactionParams as r, modalBuilder as s, SiweAuthenticateModalParams as t, sendInteraction as u, getMergeToken as v, displaySharingPage as x, getMerchantInformation as y };
|
|
648
|
+
//# sourceMappingURL=index-BCoPwxjq.d.cts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { f as Currency, h as Language, p as FrakWalletSdkConfig, u as TokenAmountType, v as AttributionDefaults, y as AttributionParams } from "./merchantInformation--tLnowAN.js";
|
|
2
|
+
import { G as MerchantConfigResponse, J as SdkResolvedConfig, L as PrepareSsoParamsType, a as FrakContextV1, i as FrakContext, o as FrakContextV2, s as FrakClient, z as SsoMetadata } from "./openSso-CENLMWJx.js";
|
|
2
3
|
import { Hex } from "viem";
|
|
3
4
|
|
|
4
5
|
//#region src/types/compression.d.ts
|
|
@@ -320,7 +321,7 @@ type SdkComponentEventMap = {
|
|
|
320
321
|
type SdkHandshakeFailureReason = "timeout" | "origin" | "asset_push" | "unknown";
|
|
321
322
|
type SdkLifecycleEventMap = {
|
|
322
323
|
sdk_initialized: {
|
|
323
|
-
|
|
324
|
+
sdk_version?: string;
|
|
324
325
|
};
|
|
325
326
|
sdk_iframe_connected: {
|
|
326
327
|
handshake_duration_ms: number;
|
|
@@ -343,9 +344,9 @@ type SdkLifecycleEventMap = {
|
|
|
343
344
|
type SdkReferralEventMap = {
|
|
344
345
|
user_referred_started: {
|
|
345
346
|
referrer?: string;
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
347
|
+
referrer_client_id?: string;
|
|
348
|
+
referrer_wallet?: string;
|
|
349
|
+
wallet_status?: string;
|
|
349
350
|
};
|
|
350
351
|
user_referred_completed: {
|
|
351
352
|
status: "success";
|
|
@@ -644,4 +645,5 @@ declare function getQueryParamCaseInsensitive(searchParams: URLSearchParams, key
|
|
|
644
645
|
*/
|
|
645
646
|
declare function deleteQueryParamCaseInsensitive(searchParams: URLSearchParams, key: string): void;
|
|
646
647
|
//#endregion
|
|
647
|
-
export { DEEP_LINK_SCHEME as A, triggerDeepLinkWithFallback as C, MergeAttributionInput as D, SdkHandshakeFailureReason as E, createIFrameFrakClient as F, CompressedData as I, HashProtectedData as L, getClientId as M, getBackendUrl as N, mergeAttribution as O, setupClient as P, KeyProvider as R, DeepLinkFallbackOptions as S, SdkEventMap as T, withCache as _, FullSsoParams as a, isMobile as b, findIframeInOpener as c, formatAmount as d, decompressJsonFromB64 as f, clearAllCache as g, base64urlEncode as h, CompressedSsoData as i, sdkConfigStore as j, FrakContextManager as k, getSupportedCurrency as l, base64urlDecode as m, getQueryParamCaseInsensitive as n, generateSsoUrl as o, compressJsonToB64 as p, AppSpecificSsoMetadata as r, baseIframeProps as s, deleteQueryParamCaseInsensitive as t, getCurrencyAmountKey as u, isIOS as v, trackEvent as w, redirectToExternalBrowser as x, isInAppBrowser as y };
|
|
648
|
+
export { DEEP_LINK_SCHEME as A, triggerDeepLinkWithFallback as C, MergeAttributionInput as D, SdkHandshakeFailureReason as E, createIFrameFrakClient as F, CompressedData as I, HashProtectedData as L, getClientId as M, getBackendUrl as N, mergeAttribution as O, setupClient as P, KeyProvider as R, DeepLinkFallbackOptions as S, SdkEventMap as T, withCache as _, FullSsoParams as a, isMobile as b, findIframeInOpener as c, formatAmount as d, decompressJsonFromB64 as f, clearAllCache as g, base64urlEncode as h, CompressedSsoData as i, sdkConfigStore as j, FrakContextManager as k, getSupportedCurrency as l, base64urlDecode as m, getQueryParamCaseInsensitive as n, generateSsoUrl as o, compressJsonToB64 as p, AppSpecificSsoMetadata as r, baseIframeProps as s, deleteQueryParamCaseInsensitive as t, getCurrencyAmountKey as u, isIOS as v, trackEvent as w, redirectToExternalBrowser as x, isInAppBrowser as y };
|
|
649
|
+
//# sourceMappingURL=index-CiHjUnfc.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { f as Currency, h as Language, p as FrakWalletSdkConfig, u as TokenAmountType, v as AttributionDefaults, y as AttributionParams } from "./merchantInformation-BSuahpjl.cjs";
|
|
2
|
+
import { G as MerchantConfigResponse, J as SdkResolvedConfig, L as PrepareSsoParamsType, a as FrakContextV1, i as FrakContext, o as FrakContextV2, s as FrakClient, z as SsoMetadata } from "./openSso-CKvkAGts.cjs";
|
|
2
3
|
import { Hex } from "viem";
|
|
3
4
|
|
|
4
5
|
//#region src/types/compression.d.ts
|
|
@@ -320,7 +321,7 @@ type SdkComponentEventMap = {
|
|
|
320
321
|
type SdkHandshakeFailureReason = "timeout" | "origin" | "asset_push" | "unknown";
|
|
321
322
|
type SdkLifecycleEventMap = {
|
|
322
323
|
sdk_initialized: {
|
|
323
|
-
|
|
324
|
+
sdk_version?: string;
|
|
324
325
|
};
|
|
325
326
|
sdk_iframe_connected: {
|
|
326
327
|
handshake_duration_ms: number;
|
|
@@ -343,9 +344,9 @@ type SdkLifecycleEventMap = {
|
|
|
343
344
|
type SdkReferralEventMap = {
|
|
344
345
|
user_referred_started: {
|
|
345
346
|
referrer?: string;
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
347
|
+
referrer_client_id?: string;
|
|
348
|
+
referrer_wallet?: string;
|
|
349
|
+
wallet_status?: string;
|
|
349
350
|
};
|
|
350
351
|
user_referred_completed: {
|
|
351
352
|
status: "success";
|
|
@@ -644,4 +645,5 @@ declare function getQueryParamCaseInsensitive(searchParams: URLSearchParams, key
|
|
|
644
645
|
*/
|
|
645
646
|
declare function deleteQueryParamCaseInsensitive(searchParams: URLSearchParams, key: string): void;
|
|
646
647
|
//#endregion
|
|
647
|
-
export { DEEP_LINK_SCHEME as A, triggerDeepLinkWithFallback as C, MergeAttributionInput as D, SdkHandshakeFailureReason as E, createIFrameFrakClient as F, CompressedData as I, HashProtectedData as L, getClientId as M, getBackendUrl as N, mergeAttribution as O, setupClient as P, KeyProvider as R, DeepLinkFallbackOptions as S, SdkEventMap as T, withCache as _, FullSsoParams as a, isMobile as b, findIframeInOpener as c, formatAmount as d, decompressJsonFromB64 as f, clearAllCache as g, base64urlEncode as h, CompressedSsoData as i, sdkConfigStore as j, FrakContextManager as k, getSupportedCurrency as l, base64urlDecode as m, getQueryParamCaseInsensitive as n, generateSsoUrl as o, compressJsonToB64 as p, AppSpecificSsoMetadata as r, baseIframeProps as s, deleteQueryParamCaseInsensitive as t, getCurrencyAmountKey as u, isIOS as v, trackEvent as w, redirectToExternalBrowser as x, isInAppBrowser as y };
|
|
648
|
+
export { DEEP_LINK_SCHEME as A, triggerDeepLinkWithFallback as C, MergeAttributionInput as D, SdkHandshakeFailureReason as E, createIFrameFrakClient as F, CompressedData as I, HashProtectedData as L, getClientId as M, getBackendUrl as N, mergeAttribution as O, setupClient as P, KeyProvider as R, DeepLinkFallbackOptions as S, SdkEventMap as T, withCache as _, FullSsoParams as a, isMobile as b, findIframeInOpener as c, formatAmount as d, decompressJsonFromB64 as f, clearAllCache as g, base64urlEncode as h, CompressedSsoData as i, sdkConfigStore as j, FrakContextManager as k, getSupportedCurrency as l, base64urlDecode as m, getQueryParamCaseInsensitive as n, generateSsoUrl as o, compressJsonToB64 as p, AppSpecificSsoMetadata as r, baseIframeProps as s, deleteQueryParamCaseInsensitive as t, getCurrencyAmountKey as u, isIOS as v, trackEvent as w, redirectToExternalBrowser as x, isInAppBrowser as y };
|
|
649
|
+
//# sourceMappingURL=index-D25z5B9K.d.cts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as GetMerchantInformationReturnType } from "./merchantInformation--tLnowAN.js";
|
|
2
|
+
import { B as FinalActionType, C as DisplayModalParamsType, D as ModalStepTypes, E as ModalRpcStepsResultType, L as PrepareSsoParamsType, M as SiweAuthenticateReturnType, N as SiweAuthenticationParams, O as SendTransactionModalStepType, P as LoginModalStepType, R as PrepareSsoReturnType, V as FinalModalStepType, b as DisplaySharingPageParamsType, d as WalletStatusReturnType, f as UserReferralStatusType, h as DisplayEmbeddedWalletResultType, i as FrakContext, k as SendTransactionReturnType, m as DisplayEmbeddedWalletParamsType, p as SendInteractionParamsType, s as FrakClient, w as ModalRpcMetadata, x as DisplaySharingPageResultType } from "./openSso-CENLMWJx.js";
|
|
2
3
|
|
|
3
4
|
//#region src/actions/displayEmbeddedWallet.d.ts
|
|
4
5
|
/**
|
|
@@ -643,4 +644,5 @@ declare function siweAuthenticate(client: FrakClient, {
|
|
|
643
644
|
metadata
|
|
644
645
|
}: SiweAuthenticateModalParams): Promise<SiweAuthenticateReturnType>;
|
|
645
646
|
//#endregion
|
|
646
|
-
export { displayEmbeddedWallet as C, displayModal as S, getUserReferralStatus as _, ModalBuilder as a, ensureIdentity as b, watchWalletStatus as c, REFERRAL_SUCCESS_EVENT as d, setupReferral as f, prepareSso as g, processReferral as h, sendTransaction as i, trackPurchaseStatus as l, ProcessReferralOptions as m, siweAuthenticate as n, ModalStepBuilder as o, referralInteraction as p, SendTransactionParams as r, modalBuilder as s, SiweAuthenticateModalParams as t, sendInteraction as u, getMergeToken as v, displaySharingPage as x, getMerchantInformation as y };
|
|
647
|
+
export { displayEmbeddedWallet as C, displayModal as S, getUserReferralStatus as _, ModalBuilder as a, ensureIdentity as b, watchWalletStatus as c, REFERRAL_SUCCESS_EVENT as d, setupReferral as f, prepareSso as g, processReferral as h, sendTransaction as i, trackPurchaseStatus as l, ProcessReferralOptions as m, siweAuthenticate as n, ModalStepBuilder as o, referralInteraction as p, SendTransactionParams as r, modalBuilder as s, SiweAuthenticateModalParams as t, sendInteraction as u, getMergeToken as v, displaySharingPage as x, getMerchantInformation as y };
|
|
648
|
+
//# sourceMappingURL=index-trhAN7q9.d.ts.map
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./frakContext-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./frakContext-CeVlWFcX.cjs`),t=require(`./src-TpX-7MwJ.cjs`),n=require(`./getCurrencyAmountKey-h2eOiYzv.cjs`);exports.DEEP_LINK_SCHEME=t.f,exports.FrakContextManager=e.t,exports.base64urlDecode=e.p,exports.base64urlEncode=e.m,exports.baseIframeProps=t.a,exports.clearAllCache=e._,exports.compressJsonToB64=e.f,exports.createIFrameFrakClient=t.i,exports.decompressJsonFromB64=t.r,exports.deleteQueryParamCaseInsensitive=e.a,exports.findIframeInOpener=t.o,exports.formatAmount=n.n,exports.generateSsoUrl=e.d,exports.getBackendUrl=e.g,exports.getClientId=e.y,exports.getCurrencyAmountKey=n.t,exports.getQueryParamCaseInsensitive=e.o,exports.getSupportedCurrency=n.r,exports.isIOS=t.s,exports.isInAppBrowser=t.c,exports.isMobile=t.l,exports.mergeAttribution=t.t,exports.redirectToExternalBrowser=t.u,exports.sdkConfigStore=e.h,exports.setupClient=t.n,exports.ssoPopupFeatures=e.l,exports.ssoPopupName=e.u,exports.trackEvent=e.s,exports.triggerDeepLinkWithFallback=t.d,exports.withCache=e.v;
|