@fruga/sdk 1.0.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- var xe=Object.defineProperty;var Ce=(a,e,t)=>e in a?xe(a,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[e]=t;var D=(a,e,t)=>Ce(a,typeof e!="symbol"?e+"":e,t);import{useEffect as pe,useRef as We}from"react";var K={widgetTitle:"Savings Box",launcherLabel:"Insurance Savings",launcherIcon:"arrow",showLauncher:!0,mode:"light",allowUserThemeControl:!1,defaultTheme:"light",uiVersion:"current",primary:"#1E40AF",accent:"#2648C9",accentTextLight:"#FFFFFF",accentTextDark:"#FFFFFF",bg:"#0B1220",panel:"#0F1A2E",panel2:"#111F38",itemBg:"rgba(255,255,255,0.03)",text:"#E6EDF7",muted:"#A7B3C6",border:"rgba(255,255,255,0.08)",success:"#34D399",danger:"#EF4444",warning:"#FBBF24",lightBg:"#F8FAFC",lightPanel:"#FFFFFF",lightPanel2:"#F1F5F9",lightItemBg:"rgba(0,0,0,0.04)",lightText:"#0F172A",lightMuted:"#64748B",lightBorder:"rgba(0,0,0,0.1)"};function Z(a){let e=a.mode==="light",t=e?a.textColorLight?.trim()||a.lightText:a.textColorDark?.trim()||a.text;return{...a,bg:e?a.lightBg:a.bg,panel:e?a.lightPanel:a.panel,panel2:e?a.lightPanel2:a.panel2,itemBg:e?a.lightItemBg:a.itemBg,text:t,muted:e?a.lightMuted:a.muted,border:e?a.lightBorder:a.border,subtleBg:e?"rgba(0,0,0,0.02)":"rgba(255,255,255,0.03)",tooltipBg:e?"#FFFFFF":"#0B1220",tooltipBorder:e?"rgba(0,0,0,0.12)":"rgba(255,255,255,0.12)",accentText:e?a.accentTextLight?.trim()||"#0F172A":a.accentTextDark?.trim()||"#FFFFFF",isLight:e,mode:a.mode}}import{jsx as ra}from"react/jsx-runtime";import{useState as le,useEffect as re,useRef as se}from"react";import{forwardRef as Pe,createElement as Ae}from"react";var R=(...a)=>a.filter((e,t,o)=>!!e&&e.trim()!==""&&o.indexOf(e)===t).join(" ").trim();var _=a=>a.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase();var Q=a=>a.replace(/^([A-Z])|[\s-_]+(\w)/g,(e,t,o)=>o?o.toUpperCase():t.toLowerCase());var G=a=>{let e=Q(a);return e.charAt(0).toUpperCase()+e.slice(1)};import{forwardRef as ke,createElement as j}from"react";var b={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};var J=a=>{for(let e in a)if(e.startsWith("aria-")||e==="role"||e==="title")return!0;return!1};import{createContext as he,useContext as Se,useMemo as xa,createElement as Ca}from"react";var we=he({});var $=()=>Se(we);var Y=ke(({color:a,size:e,strokeWidth:t,absoluteStrokeWidth:o,className:d="",children:u,iconNode:c,...s},i)=>{let{size:l=24,strokeWidth:r=2,absoluteStrokeWidth:L=!1,color:C="currentColor",className:p=""}=$()??{},f=o??L?Number(t??r)*24/Number(e??l):t??r;return j("svg",{ref:i,...b,width:e??l??b.width,height:e??l??b.height,stroke:a??C,strokeWidth:f,className:R("lucide",p,d),...!u&&!J(s)&&{"aria-hidden":"true"},...s},[...c.map(([h,I])=>j(h,I)),...Array.isArray(u)?u:[u]])});var x=(a,e)=>{let t=Pe(({className:o,...d},u)=>Ae(Y,{ref:u,iconNode:e,className:R(`lucide-${_(G(a))}`,`lucide-${a}`,o),...d}));return t.displayName=G(a),t};var Be=[["rect",{width:"20",height:"14",x:"2",y:"5",rx:"2",key:"ynyp8z"}],["line",{x1:"2",x2:"22",y1:"10",y2:"10",key:"1b3vmo"}]],B=x("credit-card",Be);var Fe=[["path",{d:"M12 7v14",key:"1akyts"}],["path",{d:"M20 11v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-8",key:"1sqzm4"}],["path",{d:"M7.5 7a1 1 0 0 1 0-5A4.8 8 0 0 1 12 7a4.8 8 0 0 1 4.5-5 1 1 0 0 1 0 5",key:"kc0143"}],["rect",{x:"3",y:"7",width:"18",height:"4",rx:"1",key:"1hberx"}]],F=x("gift",Fe);var ye=[["path",{d:"M13 5H19V11",key:"1n1gyv"}],["path",{d:"M19 5L5 19",key:"72u4yj"}]],y=x("move-up-right",ye);var Me=[["path",{d:"M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z",key:"1s2grr"}],["path",{d:"M20 2v4",key:"1rf3ol"}],["path",{d:"M22 4h-4",key:"gwowj6"}],["circle",{cx:"4",cy:"20",r:"2",key:"6kqj1y"}]],g=x("sparkles",Me);var Te=[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]],M=x("x",Te);import{jsx as q}from"react/jsx-runtime";function ee({theme:a}){return a.launcherIcon==="none"?null:a.launcherIcon==="sparkles"?q(g,{size:20}):a.launcherIcon==="credit"?q(B,{size:20}):a.launcherIcon==="arrow"?q(y,{size:20}):q(F,{size:20})}import{useEffect as qe,useRef as ve}from"react";var ae=a=>{let e,t=new Set,o=(l,r)=>{let L=typeof l=="function"?l(e):l;if(!Object.is(L,e)){let C=e;e=r??(typeof L!="object"||L===null)?L:Object.assign({},e,L),t.forEach(p=>p(e,C))}},d=()=>e,s={setState:o,getState:d,getInitialState:()=>i,subscribe:l=>(t.add(l),()=>t.delete(l))},i=e=a(o,d,s);return s},te=(a=>a?ae(a):ae);import v from"react";var De=a=>a;function Re(a,e=De){let t=v.useSyncExternalStore(a.subscribe,v.useCallback(()=>e(a.getState()),[a,e]),v.useCallback(()=>e(a.getInitialState()),[a,e]));return v.useDebugValue(t),t}var oe=a=>{let e=te(a),t=o=>Re(e,o);return Object.assign(t,e),t},ue=(a=>a?oe(a):oe);var w=ue(a=>({isOpen:!1,isFocused:!0,isIdle:!1,available:0,pending:0,mode:"light",token:null,partnerPayout:null,isAddingPayoutFromAlert:!1,showPayoutSuccess:!1,toggle:()=>a(e=>({isOpen:!e.isOpen})),setOpen:e=>a({isOpen:e}),setFocused:e=>a({isFocused:e}),setIdle:e=>a({isIdle:e}),setBalances:(e,t)=>a({available:e,pending:t}),setMode:e=>a({mode:e}),setToken:e=>a({token:e}),setPartnerPayout:e=>a({partnerPayout:e}),setIsAddingPayoutFromAlert:e=>a({isAddingPayoutFromAlert:e}),setShowPayoutSuccess:e=>a({showPayoutSuccess:e})}));function de(){let{setFocused:a,setIdle:e}=w(),t=ve(null);qe(()=>{let o=()=>a(!0),d=()=>a(!1),u=()=>{e(!1),t.current&&clearTimeout(t.current),t.current=setTimeout(()=>{e(!0)},6e4)},c=()=>{u()};u(),a(document.hasFocus()),window.addEventListener("focus",o),window.addEventListener("blur",d);let s=["mousemove","keydown","scroll","touchstart","mousedown"];return s.forEach(i=>{window.addEventListener(i,c,i==="scroll"?!0:void 0)}),()=>{window.removeEventListener("focus",o),window.removeEventListener("blur",d),s.forEach(i=>{window.removeEventListener(i,c,i==="scroll"?!0:void 0)}),t.current&&clearTimeout(t.current)}},[a,e])}import{Fragment as Ue,jsx as P,jsxs as V}from"react/jsx-runtime";var fe=({children:a,theme:e,hostHeight:t=800,isMobile:o=!1})=>{de();let{isOpen:d,toggle:u,mode:c}=w(),s=e||K,[i,l]=le(t),[r,L]=le(o),C=s.allowUserThemeControl?{...s,mode:c}:{...s,mode:s.mode||"light"},p=Z(C),f=se(null),h=se(null);return re(()=>{let I=m=>{if(m.data?.type==="FRUGA_HOST_RESIZE"&&(l(m.data.hostHeight),L(m.data.isHostMobile)),m.data?.type==="FRUGA_TOKEN_UPDATE"){let{setToken:n}=w.getState();n(m.data.token)}if(m.data?.type==="FRUGA_GET_BALANCE"){let{available:n,pending:S}=w.getState();console.log("FRUGA_GET_BALANCE",n,S),window.parent.postMessage({type:"FRUGA_BALANCE_RESPONSE",requestId:m.data.requestId,available:n,pending:S},"*")}};return window.addEventListener("message",I),()=>window.removeEventListener("message",I)},[]),re(()=>{if(!f.current)return;let I=()=>{if(!f.current)return;let n=d?450:100;!d&&h.current&&(n=h.current.offsetWidth+40);let S=d?Math.max(200,i-100):100;r&&d&&(n=window.innerWidth,S=i),window.parent.postMessage({type:"FRUGA_RESIZE",height:S,width:n,isFullscreen:r&&d},"*")},m=new ResizeObserver(()=>I());return m.observe(f.current),I(),()=>m.disconnect()},[d,i,r]),V("div",{ref:f,className:p.mode==="dark"?"dark":"",style:{fontFamily:'ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',color:p.text,display:"flex",flexDirection:"column",alignItems:"flex-end",justifyContent:"flex-end",padding:r&&d?"0":"20px",boxSizing:"border-box",height:"100%",margin:0,overscrollBehavior:"contain"},children:[d&&P("div",{style:{width:r?"100vw":"410px",minWidth:r?"100vw":"410px",flexShrink:0,maxHeight:r?"100vh":`${Math.max(100,i-100)}px`,height:"100%",marginBottom:r?"0":"20px",backgroundColor:p.accent,borderRadius:r?"0":"16px",boxShadow:r?"none":"0 20px 40px -10px rgba(0, 0, 0, 0.15), 0 0 1px rgba(0,0,0,0.1)",display:"flex",flexDirection:"column",overflow:"hidden",zIndex:9998},children:P("div",{className:"fruga-scrollbar",style:{flex:1,minHeight:0,backgroundColor:p.isLight?"#f9f9f9":"#111",display:"flex",flexDirection:"column",boxShadow:r?"none":"0 20px 40px -10px rgba(0, 0, 0, 0.15), 0 0 1px rgba(0,0,0,0.1)"},children:a})}),V("button",{ref:h,onClick:u,style:{backgroundColor:p.accent,color:p.accentText,border:"none",cursor:"pointer",boxShadow:"0 12px 30px -5px rgba(0, 0, 0, 0.1), 0 0 1px rgba(0,0,0, 0.1)",display:d?"none":"flex",alignItems:"center",justifyContent:"center",zIndex:9999,flexShrink:0,borderRadius:"20px",padding:"10px 16px",transition:"transform 0.3s ease-in-out"},className:"flex h-15 items-center gap-2 rounded-xl px-4 active:scale-[0.99]","aria-label":d?"Close widget":"Open widget",children:[s.launcherLabel&&!d?V(Ue,{children:[s.launcherLabel!=="none"&&P("div",{className:"text-sm font-semibold",children:s.launcherLabel}),P("div",{className:"flex h-6 w-5 shrink-0 items-center justify-center",children:P(ee,{theme:p})})]}):null,d?P(M,{}):null]})]})};import{Fragment as St,jsx as wt}from"react/jsx-runtime";import{useEffect as At,useState as Bt}from"react";var T={BOOTSTRAP_DATA:"fruga_bootstrap",BOOTSTRAP_CONFIG:"fruga_active_config"},k=class{static set(e,t){if(!(typeof window>"u"))try{window.localStorage.setItem(e,JSON.stringify(t))}catch(o){console.warn(`[FrugaStorage] Failed to save key "${e}":`,o)}}static get(e){if(typeof window>"u")return null;try{let t=window.localStorage.getItem(e);return t?JSON.parse(t):null}catch(t){return console.warn(`[FrugaStorage] Failed to load key "${e}":`,t),null}}static remove(e){typeof window>"u"||window.localStorage.removeItem(e)}static clear(){typeof window>"u"||window.localStorage.clear()}};var Oe=typeof process<"u"&&process.env.VITE_FRUGA_API_URL||"https://api-dev.fruga.co.uk",ie=a=>a||Oe;var z={partnerEnvId:"env_dev_123",partnerId:"partner_abc_456",partnerType:"EXTERNAL_TENANT",partnerPayout:"USER",auth:{mode:"JWT",audience:"https://api.fruga.io",issuer:"https://auth.fruga.io",tokenTtlSeconds:70},config:{ui:{accent:"#F97316",defaultTheme:"light",launcherIcon:"arrow",showLauncher:!0,launcherLabel:"Help",accentTextDark:"#832424",accentTextLight:"#d81414",allowUserThemeControl:!0},validValues:{defaultTheme:["light","dark"],launcherIcon:["none","gift","sparkles","credit","arrow"]}}};var U=class{async bootstrap(e,t){let o=ie(t);if(typeof process<"u"&&process.env.VITE_FRUGA_MODE==="test")return console.log("Fruga SDK: Using bootstrap mocks (VITE_FRUGA_MODE=test)"),z;try{let u=await fetch(`${o}/widget/bootstrap?partnerKey=${e}`,{method:"GET",headers:{Accept:"application/json","Content-Type":"application/json"}});if(!u.ok)throw new Error(`Bootstrap failed with status: ${u.status}`);return await u.json()}catch(u){return console.error("Fruga SDK: Bootstrap failed, falling back to mocks",u),z}}};var O=class{mount(e,t,o){let{partnerKey:d,theme:u,primaryColor:c,appUrl:s,userId:i}=o,l=document.createElement("iframe"),r=t.partnerType==="EXTERNAL_TENANT"?"relay":"connect",L=typeof document<"u"?document.currentScript:null,C=L?.src||(typeof window<"u"?window.location.href:""),p=`./${r}/index.html`;if(!L)p=`https://frugainsurance.github.io/sdk/${r}/index.html`;else try{new URL(C).pathname.includes("/dist/")&&(p=`./${r}/index.html`)}catch{}let f=new URL(s||t.appUrl||p,C);f.searchParams.set("partnerKey",d),f.searchParams.set("partnerId",t.partnerId);let h=typeof window<"u"?window.innerWidth<768:!1;if(f.searchParams.set("isHostMobile",String(h)),typeof window<"u"&&(f.searchParams.set("hostHeight",String(window.innerHeight)),f.searchParams.set("hostWidth",String(window.innerWidth))),u&&f.searchParams.set("theme",u),c&&f.searchParams.set("primaryColor",c),i&&f.searchParams.set("userId",i),t.token&&f.searchParams.set("token",t.token),o.baseUrl&&f.searchParams.set("baseUrl",o.baseUrl),t.bootstrapConfig)try{let n=btoa(JSON.stringify(t.bootstrapConfig));f.searchParams.set("bootstrapConfig",n)}catch(n){console.warn("FrugaLoader: Failed to encode bootstrapConfig for iframe URL",n)}l.src=f.toString(),l.style.border="none",l.style.width="410px",l.style.height="600px",l.style.backgroundColor="transparent";let I=n=>{if(n.source===l.contentWindow&&n.data?.type==="FRUGA_RESIZE"){let{width:S,height:Le,isFullscreen:Ie}=n.data;Ie?(l.style.position="fixed",l.style.inset="0",l.style.width="100vw",l.style.height="100vh",l.style.zIndex="99999"):(l.style.position="static",l.style.width=`${S}px`,l.style.height=`${Le}px`,l.style.zIndex="auto")}},m=()=>{l.contentWindow?.postMessage({type:"FRUGA_HOST_RESIZE",isHostMobile:window.innerWidth<768,hostHeight:window.innerHeight,hostWidth:window.innerWidth},"*")};return window.addEventListener("message",I),window.addEventListener("resize",m),e.appendChild(l),{iframe:l,cleanup:()=>{window.removeEventListener("message",I),window.removeEventListener("resize",m)}}}unmount(e){e.remove()}};var H=class{constructor(e,t){this.bootstrapService=e;this.widgetMounter=t;D(this,"state",{container:null,iframe:null,options:null,currentToken:null});D(this,"pendingRequests",new Map);D(this,"handleIframeMessage",async e=>{if(!(this.state.iframe&&e.source!==this.state.iframe.contentWindow)){if(e.data?.type==="NEED_TOKEN"&&this.state.options?.onTokenRequired)try{let t=await this.state.options.onTokenRequired();this.state.currentToken=t,this.state.iframe?.contentWindow?.postMessage({type:"FRUGA_TOKEN_UPDATE",token:t},"*")}catch(t){console.error("FrugaLoader: Failed to refresh token:",t)}if(e.data?.type==="FRUGA_BALANCE_RESPONSE"){let{requestId:t,available:o,pending:d}=e.data;if(t&&this.pendingRequests.has(t)){let{resolve:u,timeout:c}=this.pendingRequests.get(t);clearTimeout(c),this.pendingRequests.delete(t),u({available:o,pending:d})}}}})}async init(e){if(this.state.options=e,e.autoMount!==!1){if(e.token&&(this.state.currentToken=e.token),e.onTokenRequired)try{this.state.currentToken=await e.onTokenRequired()}catch(o){console.error("FrugaLoader: Failed to fetch initial token:",o)}await this.mount()}}setOptions(e){this.state.options=e}async mount(){if(!this.state.options||this.state.container&&this.state.iframe&&document.body.contains(this.state.container))return;let e=k.get(T.BOOTSTRAP_DATA);if(e)this.doMount(e);else{let t=k.get(T.BOOTSTRAP_CONFIG);if(t){let o={partnerEnvId:"cached",partnerId:"cached",partnerType:"EXTERNAL_TENANT",partnerPayout:"USER",auth:{mode:"none",audience:"",issuer:"",tokenTtlSeconds:0},config:t};this.doMount(o)}}try{let t=await this.bootstrapService.bootstrap(this.state.options.partnerKey,this.state.options.baseUrl);k.set(T.BOOTSTRAP_DATA,t),k.set(T.BOOTSTRAP_CONFIG,t.config),this.doMount(t)}catch{}}doMount(e){if(!this.state.options)return;let{containerId:t="fruga-widget-root"}=this.state.options,o=document.getElementById(t);o?o.innerHTML="":(o=document.createElement("div"),o.id=t,document.body.appendChild(o)),o.style.position="fixed",o.style.bottom="0px",o.style.right="0px",o.style.zIndex="9999";let u={...{...this.state.options,containerId:this.state.options.containerId||"fruga-widget-root",bootstrapConfig:e,token:this.state.currentToken||void 0},partnerId:e.partnerId,partnerType:e.partnerType},{iframe:c,cleanup:s}=this.widgetMounter.mount(o,u,this.state.options);this.state.container=o,this.state.iframe=c,o._frugaCleanup=s,window.addEventListener("message",this.handleIframeMessage)}unmount(){if(this.state.container&&document.body.contains(this.state.container)){let e=this.state.container._frugaCleanup;e?.(),this.widgetMounter.unmount(this.state.container)}this.state.container=null,this.state.iframe=null,window.removeEventListener("message",this.handleIframeMessage)}requestBalance(){this.state.iframe&&this.state.iframe.contentWindow?.postMessage({type:"FRUGA_GET_BALANCE"},"*")}async getBalance(){if(!this.state.iframe)throw new Error("FrugaLoader: Cannot get balance - widget not mounted");let e=Math.random().toString(36).substring(2,15);return new Promise((t,o)=>{let d=setTimeout(()=>{this.pendingRequests.has(e)&&(this.pendingRequests.delete(e),o(new Error("FrugaLoader: Get balance request timed out")))},5e3);this.pendingRequests.set(e,{resolve:t,reject:o,timeout:d}),this.state.iframe?.contentWindow?.postMessage({type:"FRUGA_GET_BALANCE",requestId:e},"*")})}};var He=new U,Ee=new O,A=new H(He,Ee),N=async()=>{await A.mount()},W=()=>{A.unmount()},E=async a=>{await A.init(a)},X=a=>{A.setOptions(a)},ne=()=>{A.requestBalance()},ce=async()=>A.getBalance();if(typeof window<"u"){let a=document.currentScript;if(a){let e=a.getAttribute("data-partner-key"),t=a.getAttribute("data-container-id")||void 0,o=a.getAttribute("data-app-url")||void 0,d=a.getAttribute("data-base-url")||void 0,u=window.frugaConfig||{};(e||u.partnerKey)&&E({partnerKey:e||u.partnerKey||"demo-key",containerId:t||u.containerId,appUrl:o||u.appUrl,baseUrl:d||u.baseUrl,theme:u.theme,primaryColor:u.primaryColor})}window.FrugaLoader={init:E,setOptions:X,mount:N,unmount:W,requestBalance:ne,getBalance:ce}}import{jsx as me}from"react/jsx-runtime";var Ge=a=>{let e=We(!1);return pe(()=>{e.current&&X(a)},[a]),pe(()=>(e.current||E(a).then(()=>{e.current=!0}),()=>{W(),e.current=!1}),[a.partnerKey,a.appUrl]),{mount:N,unmount:W}},Lo=a=>{let e=a.containerId||"fruga-widget-container";return Ge({...a,containerId:e,autoMount:!0}),me("div",{id:e,style:{width:"100%",height:"100%"}})},Io=({children:a,...e})=>me(fe,{...e,children:a});export{Lo as FrugaWidget,Io as WidgetUI,ce as getBalance,ne as requestBalance,Ge as useFrugaWidget};
1
+ var Ne=Object.defineProperty;var ze=(t,e,a)=>e in t?Ne(t,e,{enumerable:!0,configurable:!0,writable:!0,value:a}):t[e]=a;var l=(t,e,a)=>ze(t,typeof e!="symbol"?e+"":e,a);import{useCallback as Ba,useEffect as A,useMemo as Ra,useRef as ne,useState as _e}from"react";var Xe=["loader.global.js","native.global.js"],Ke=()=>{if(typeof document>"u")return null;let e=document.currentScript?.src;if(!e)return null;try{let a=new URL(e,document.baseURI),o=a.pathname.split("/").pop()??"";return Xe.includes(o)?a.toString():null}catch{return null}},J=Ke();var pe={widgetTitle:"Savings Box",launcherLabel:"Insurance Savings",launcherIcon:"arrow",showLauncher:!0,mode:"light",allowUserThemeControl:!1,defaultTheme:"light",uiVersion:"current",primary:"#1E40AF",accent:"#2648C9",accentTextLight:"#FFFFFF",accentTextDark:"#FFFFFF",bg:"#0B1220",panel:"#0F1A2E",panel2:"#111F38",itemBg:"rgba(255,255,255,0.03)",text:"#E6EDF7",muted:"#A7B3C6",border:"rgba(255,255,255,0.08)",success:"#34D399",danger:"#EF4444",warning:"#FBBF24",lightBg:"#F8FAFC",lightPanel:"#FFFFFF",lightPanel2:"#F1F5F9",lightItemBg:"rgba(0,0,0,0.04)",lightText:"#0F172A",lightMuted:"#64748B",lightBorder:"rgba(0,0,0,0.1)"};function me(t){let e=t.mode==="light",a=e?t.textColorLight?.trim()||t.lightText:t.textColorDark?.trim()||t.text;return{...t,bg:e?t.lightBg:t.bg,panel:e?t.lightPanel:t.panel,panel2:e?t.lightPanel2:t.panel2,itemBg:e?t.lightItemBg:t.itemBg,text:a,muted:e?t.lightMuted:t.muted,border:e?t.lightBorder:t.border,subtleBg:e?"rgba(0,0,0,0.02)":"rgba(255,255,255,0.03)",tooltipBg:e?"#FFFFFF":"#0B1220",tooltipBorder:e?"rgba(0,0,0,0.12)":"rgba(255,255,255,0.12)",accentText:e?t.accentTextLight?.trim()||"#0F172A":t.accentTextDark?.trim()||"#FFFFFF",isLight:e,mode:t.mode}}var Ze=["NEED_TOKEN","FRUGA_TOKEN_UPDATE","FRUGA_GET_BALANCE","FRUGA_BALANCE_RESPONSE","FRUGA_RESIZE","FRUGA_HOST_RESIZE","FRUGA_READY","FRUGA_INIT"],Qe=t=>typeof t=="string"&&Ze.includes(t),$e=["ttl","unauthorized"],Le=t=>{if(typeof t!="object"||t===null)return!1;let e=t;if(!Qe(e.type)||e.version!==void 0&&typeof e.version!="string")return!1;if(e.type==="NEED_TOKEN"){let{reason:a}=t;return typeof a=="string"&&$e.includes(a)}return!0},Y=t=>Number.parseInt(t.split(".")[0]??"",10);var Ie=t=>t==="PARTNER"||t==="USER"?t:null;var Je="*",F=class{constructor(e){l(this,"target");l(this,"acceptSource");l(this,"listenOn");l(this,"handlers",new Set);l(this,"pinOriginFrom");l(this,"targetOrigin");l(this,"acceptOrigin");l(this,"pinned",null);l(this,"disposed",!1);l(this,"onMessage",e=>{if(this.disposed||this.acceptSource&&e.source!==this.acceptSource||this.acceptOrigin&&e.origin!==this.acceptOrigin||!Le(e.data)||!this.admitWhileUnpinned(e.data.type,e.origin))return;let a=typeof e.data.version=="string"&&e.data.version.length>0?e.data:{...e.data,version:"1.0"};for(let o of[...this.handlers])o(a)});let a=e.listenOn??(typeof window<"u"?window:void 0);if(!a)throw new Error("PostMessageTransport: no window to listen on; pass options.listenOn");this.target=e.target,this.targetOrigin=e.targetOrigin,this.acceptSource=e.acceptSource,this.acceptOrigin=e.acceptOrigin,this.pinOriginFrom=e.pinOriginFrom,this.listenOn=a,this.listenOn.addEventListener("message",this.onMessage)}get pinnedOrigin(){return this.pinned}pinOrigin(e){this.targetOrigin=e,this.acceptOrigin=e,this.pinned=e}send(e){if(this.disposed)return;let a=typeof e.version=="string"&&e.version.length>0?e:{...e,version:"1.0"};this.target.postMessage(a,this.targetOrigin)}subscribe(e){if(this.disposed)throw new Error("PostMessageTransport: cannot subscribe after dispose()");return this.handlers.add(e),()=>{this.handlers.delete(e)}}dispose(){this.disposed||(this.disposed=!0,this.handlers.clear(),this.listenOn.removeEventListener("message",this.onMessage))}admitWhileUnpinned(e,a){return!this.pinOriginFrom||this.pinned!==null?!0:e!==this.pinOriginFrom?!1:a&&a!=="null"?(this.pinOrigin(a),!0):(this.pinned=Je,!0)}};var xe=t=>{let e=t?.listenOn??(typeof window<"u"?window:void 0);if(!e)throw new Error("createHostTransport: no window available; pass options.listenOn");let a=e.parent!==e;return new F({target:e.parent,targetOrigin:"*",pinOriginFrom:"FRUGA_INIT",listenOn:e,...a?{acceptSource:e.parent}:{}})};import{useState as oe,useEffect as Ca,useRef as Re}from"react";import{forwardRef as oa,createElement as ra}from"react";var U=(...t)=>t.filter((e,a,o)=>!!e&&e.trim()!==""&&o.indexOf(e)===a).join(" ").trim();var ge=t=>t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase();var he=t=>t.replace(/^([A-Z])|[\s-_]+(\w)/g,(e,a,o)=>o?o.toUpperCase():a.toLowerCase());var ae=t=>{let e=he(t);return e.charAt(0).toUpperCase()+e.slice(1)};import{forwardRef as ta,createElement as we}from"react";var E={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};var Ce=t=>{for(let e in t)if(e.startsWith("aria-")||e==="role"||e==="title")return!0;return!1};import{createContext as Ye,useContext as ea,useMemo as wt,createElement as kt}from"react";var aa=Ye({});var Se=()=>ea(aa);var ke=ta(({color:t,size:e,strokeWidth:a,absoluteStrokeWidth:o,className:r="",children:u,iconNode:d,...s},f)=>{let{size:n=24,strokeWidth:i=2,absoluteStrokeWidth:p=!1,color:I="currentColor",className:L=""}=Se()??{},Q=o??p?Number(a??i)*24/Number(e??n):a??i;return we("svg",{ref:f,...E,width:e??n??E.width,height:e??n??E.height,stroke:t??I,strokeWidth:Q,className:U("lucide",L,r),...!u&&!Ce(s)&&{"aria-hidden":"true"},...s},[...d.map(([h,P])=>we(h,P)),...Array.isArray(u)?u:[u]])});var x=(t,e)=>{let a=oa(({className:o,...r},u)=>ra(ke,{ref:u,iconNode:e,className:U(`lucide-${ge(ae(t))}`,`lucide-${t}`,o),...r}));return a.displayName=ae(t),a};var ua=[["rect",{width:"20",height:"14",x:"2",y:"5",rx:"2",key:"ynyp8z"}],["line",{x1:"2",x2:"22",y1:"10",y2:"10",key:"1b3vmo"}]],B=x("credit-card",ua);var da=[["path",{d:"M12 7v14",key:"1akyts"}],["path",{d:"M20 11v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-8",key:"1sqzm4"}],["path",{d:"M7.5 7a1 1 0 0 1 0-5A4.8 8 0 0 1 12 7a4.8 8 0 0 1 4.5-5 1 1 0 0 1 0 5",key:"kc0143"}],["rect",{x:"3",y:"7",width:"18",height:"4",rx:"1",key:"1hberx"}]],R=x("gift",da);var sa=[["path",{d:"M13 5H19V11",key:"1n1gyv"}],["path",{d:"M19 5L5 19",key:"72u4yj"}]],D=x("move-up-right",sa);var la=[["path",{d:"M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z",key:"1s2grr"}],["path",{d:"M20 2v4",key:"1rf3ol"}],["path",{d:"M22 4h-4",key:"gwowj6"}],["circle",{cx:"4",cy:"20",r:"2",key:"6kqj1y"}]],w=x("sparkles",la);var fa=[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]],v=x("x",fa);import{jsx as H}from"react/jsx-runtime";function Pe({theme:t}){return t.launcherIcon==="none"?null:t.launcherIcon==="sparkles"?H(w,{size:20}):t.launcherIcon==="credit"?H(B,{size:20}):t.launcherIcon==="arrow"?H(D,{size:20}):H(R,{size:20})}import{useEffect as pa,useRef as ma}from"react";var Fe=t=>{let e,a=new Set,o=(n,i)=>{let p=typeof n=="function"?n(e):n;if(!Object.is(p,e)){let I=e;e=i??(typeof p!="object"||p===null)?p:Object.assign({},e,p),a.forEach(L=>L(e,I))}},r=()=>e,s={setState:o,getState:r,getInitialState:()=>f,subscribe:n=>(a.add(n),()=>a.delete(n))},f=e=t(o,r,s);return s},Me=(t=>t?Fe(t):Fe);import G from"react";var ia=t=>t;function na(t,e=ia){let a=G.useSyncExternalStore(t.subscribe,G.useCallback(()=>e(t.getState()),[t,e]),G.useCallback(()=>e(t.getInitialState()),[t,e]));return G.useDebugValue(a),a}var Te=t=>{let e=Me(t),a=o=>na(e,o);return Object.assign(a,e),a},Ae=(t=>t?Te(t):Te);var g=Ae(t=>({isOpen:!1,isFocused:!0,isIdle:!1,available:0,pending:0,mode:"light",token:null,tokenEpoch:0,authStatus:"pending",partnerPayout:null,isAddingPayoutFromAlert:!1,showPayoutSuccess:!1,toggle:()=>t(e=>({isOpen:!e.isOpen})),setOpen:e=>t({isOpen:e}),setFocused:e=>t({isFocused:e}),setIdle:e=>t({isIdle:e}),setBalances:(e,a)=>t({available:e,pending:a}),setMode:e=>t({mode:e}),setToken:e=>t(a=>({token:e,tokenEpoch:a.token===e?a.tokenEpoch:a.tokenEpoch+1,authStatus:e?"authenticated":"pending"})),setPartnerPayout:e=>t({partnerPayout:Ie(e)}),setIsAddingPayoutFromAlert:e=>t({isAddingPayoutFromAlert:e}),setShowPayoutSuccess:e=>t({showPayoutSuccess:e})}));function ye(){let{setFocused:t,setIdle:e}=g(),a=ma(null);pa(()=>{let o=()=>t(!0),r=()=>t(!1),u=()=>{e(!1),a.current&&clearTimeout(a.current),a.current=setTimeout(()=>{e(!0)},6e4)},d=()=>{u()};u(),t(document.hasFocus()),window.addEventListener("focus",o),window.addEventListener("blur",r);let s=["mousemove","keydown","scroll","touchstart","mousedown"];return s.forEach(f=>{window.addEventListener(f,d,f==="scroll"?!0:void 0)}),()=>{window.removeEventListener("focus",o),window.removeEventListener("blur",r),s.forEach(f=>{window.removeEventListener(f,d,f==="scroll"?!0:void 0)}),a.current&&clearTimeout(a.current)}},[t,e])}import{useContext as xa,useEffect as ga,useRef as ha}from"react";import{createContext as La}from"react";import{jsx as Ia}from"react/jsx-runtime";var te=La(null),Be=({transport:t,children:e})=>Ia(te.Provider,{value:t,children:e});var b=()=>{let t=xa(te);if(!t)throw new Error("useMessageTransport: no transport in context. Wrap the widget tree in <MessageTransportProvider transport={...}>.");return t},O=(t,e)=>{let a=b(),o=ha(e);o.current=e,ga(()=>a.subscribe(r=>{r.type===t&&o.current(r)}),[a,t])};import{Fragment as Sa,jsx as M,jsxs as re}from"react/jsx-runtime";var De=({children:t,theme:e,hostHeight:a=800,isMobile:o=!1})=>{ye();let{isOpen:r,toggle:u,mode:d}=g(),s=e||pe,[f,n]=oe(a),[i,p]=oe(o),[I,L]=oe(void 0),Q=s.allowUserThemeControl?{...s,mode:d}:{...s,mode:I??s.mode??"light"},h=me(Q),P=Re(null),$=Re(null),j=b();return O("FRUGA_HOST_RESIZE",m=>{n(m.hostHeight),p(m.isHostMobile)}),O("FRUGA_TOKEN_UPDATE",m=>{g.getState().setToken(m.token)}),O("FRUGA_GET_BALANCE",m=>{let{available:S,pending:y}=g.getState();j.send({type:"FRUGA_BALANCE_RESPONSE",version:"1.0",requestId:m.requestId,available:S,pending:y})}),O("FRUGA_INIT",m=>{n(m.host.height),p(m.host.isMobile),m.token&&g.getState().setToken(m.token),m.overrides?.panelOpen&&g.getState().setOpen(!0);let S=m.overrides?.theme;S&&(L(S),g.getState().setMode(S))}),Ca(()=>{if(!P.current)return;let m=()=>{if(!P.current)return;let y=r?450:100;!r&&$.current&&(y=$.current.offsetWidth+40);let ce=r?Math.max(200,f-100):100;i&&r&&(y=window.innerWidth,ce=f),j.send({type:"FRUGA_RESIZE",version:"1.0",height:ce,width:y,isFullscreen:i&&r})},S=new ResizeObserver(()=>m());return S.observe(P.current),m(),()=>S.disconnect()},[r,f,i,j]),re("div",{ref:P,className:h.mode==="dark"?"dark":"",style:{fontFamily:'ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',color:h.text,display:"flex",flexDirection:"column",alignItems:"flex-end",justifyContent:"flex-end",padding:i&&r?"0":"20px",boxSizing:"border-box",height:"100%",margin:0,overscrollBehavior:"contain"},children:[r&&M("div",{style:{width:i?"100vw":"410px",minWidth:i?"100vw":"410px",flexShrink:0,maxHeight:i?"100vh":`${Math.max(100,f-100)}px`,height:"100%",marginBottom:i?"0":"20px",backgroundColor:h.accent,borderRadius:i?"0":"16px",boxShadow:i?"none":"0 20px 40px -10px rgba(0, 0, 0, 0.15), 0 0 1px rgba(0,0,0,0.1)",display:"flex",flexDirection:"column",overflow:"hidden",zIndex:9998},children:M("div",{className:"fruga-scrollbar",style:{flex:1,minHeight:0,backgroundColor:h.isLight?"#f9f9f9":"#111",display:"flex",flexDirection:"column",boxShadow:i?"none":"0 20px 40px -10px rgba(0, 0, 0, 0.15), 0 0 1px rgba(0,0,0,0.1)"},children:t})}),re("button",{ref:$,onClick:u,style:{backgroundColor:h.accent,color:h.accentText,border:"none",cursor:"pointer",boxShadow:"0 12px 30px -5px rgba(0, 0, 0, 0.1), 0 0 1px rgba(0,0,0, 0.1)",display:r?"none":"flex",alignItems:"center",justifyContent:"center",zIndex:9999,flexShrink:0,borderRadius:"20px",padding:"10px 16px",transition:"transform 0.3s ease-in-out"},className:"flex h-15 items-center gap-2 rounded-xl px-4 active:scale-[0.99]","aria-label":r?"Close widget":"Open widget",children:[s.launcherLabel&&!r?re(Sa,{children:[s.launcherLabel!=="none"&&M("div",{className:"text-sm font-semibold",children:s.launcherLabel}),M("div",{className:"flex h-6 w-5 shrink-0 items-center justify-center",children:M(Pe,{theme:h})})]}):null,r?M(v,{}):null]})]})};import{Fragment as Uo,jsx as Eo}from"react/jsx-runtime";import{useEffect as Wo,useState as Vo}from"react";var ve="fruga_bootstrap",be=(t,e)=>`fruga:${t}:${e}`,T=class{static set(e,a){if(!(typeof window>"u"))try{window.localStorage.setItem(e,JSON.stringify(a))}catch(o){console.warn(`[FrugaStorage] Failed to save key "${e}":`,o)}}static get(e){if(typeof window>"u")return null;try{let a=window.localStorage.getItem(e);return a?JSON.parse(a):null}catch(a){return console.warn(`[FrugaStorage] Failed to load key "${e}":`,a),null}}static remove(e){if(!(typeof window>"u"))try{window.localStorage.removeItem(e)}catch(a){console.warn(`[FrugaStorage] Failed to remove key "${e}":`,a)}}};var wa="https://api.fruga.co.uk",Oe=t=>t||wa;var qe="1.3.0";var W=class{async bootstrap(e,a){let o=Oe(a),r=`${o}/widget/bootstrap?partnerKey=${encodeURIComponent(e)}`,u;try{u=await fetch(r,{method:"GET",headers:{Accept:"application/json"}})}catch(d){throw new Error(`FrugaLoader: bootstrap request to ${o} failed`,{cause:d})}if(!u.ok)throw new Error(`FrugaLoader: bootstrap request to ${o} failed with status ${u.status}`);try{return await u.json()}catch(d){throw new Error(`FrugaLoader: bootstrap response from ${o} was not valid JSON`,{cause:d})}}};var Ue="*",Ee=`https://cdn.fruga.co.uk/v/${qe}/`,V=class{mount(e,a,o,r){let u=document.createElement("iframe"),d=this.resolveWidgetUrl(a,o,r),s=this.originOf(d);u.src=d,u.style.border="none",u.style.width="410px",u.style.height="600px",u.style.backgroundColor="transparent",e.appendChild(u);let f=u.contentWindow;if(!f)throw new Error("FrugaLoader: iframe has no contentWindow after being appended");let n=new F({target:f,targetOrigin:s,acceptSource:f,...s===Ue?{}:{acceptOrigin:s}}),i=!1,p=n.subscribe(L=>{if(L.type==="FRUGA_READY"){i=!0;return}L.type==="FRUGA_RESIZE"&&this.applySize(u,L.width,L.height,L.isFullscreen)}),I=()=>{i&&n.send({type:"FRUGA_HOST_RESIZE",version:"1.0",isHostMobile:window.innerWidth<768,hostHeight:window.innerHeight,hostWidth:window.innerWidth})};return window.addEventListener("resize",I),{iframe:u,transport:n,widgetOrigin:s,cleanup:()=>{window.removeEventListener("resize",I),p(),n.dispose()}}}unmount(e){e.remove()}originOf(e){let a;try{a=new URL(e).origin}catch{a="null"}return a&&a!=="null"?a:(console.warn(`FrugaLoader: widget URL has an opaque origin (${e}); falling back to postMessage targetOrigin '*'`),Ue)}applySize(e,a,o,r){if(r){e.style.position="fixed",e.style.inset="0",e.style.width="100vw",e.style.height="100vh",e.style.zIndex="99999";return}e.style.position="static",e.style.inset="",e.style.width=`${a}px`,e.style.height=`${o}px`,e.style.zIndex="auto"}resolveWidgetUrl(e,a,o){let r=J??(typeof window<"u"?window.location.href:Ee),u=e.appUrl||o||(J?`./${a}/index.html`:`${Ee}${a}/index.html`),d=new URL(u,r);return d.searchParams.set("partnerKey",e.partnerKey),d.toString()}};var He=1e4,ue=t=>t==="EXTERNAL_TENANT"?"relay":"connect";var Ge="1.3.0";var N=Ge;var C=class extends Error{constructor(a,o,r={}){super(o);l(this,"code");l(this,"loaderVersion");l(this,"widgetVersion");l(this,"protocolVersion");this.name="FrugaLoadError",this.code=a,this.loaderVersion=r.loaderVersion??N,this.widgetVersion=r.widgetVersion,this.protocolVersion=r.protocolVersion}};var We="fruga-widget-root",de=5e3,Pa="Fruga widget failed to load.",Ve="data-fruga-owned",Fa=1440*60*1e3,Ma={get:t=>T.get(t),set:(t,e)=>T.set(t,e),remove:t=>T.remove(t)},z=class{constructor(e,a,o={}){this.bootstrapService=e;this.widgetMounter=a;this.env=o;l(this,"options",null);l(this,"currentToken",null);l(this,"session",null);l(this,"mountGeneration",0);l(this,"inFlightBootstrap",null);l(this,"pendingRequests",new Map);l(this,"loadFailed",!1);l(this,"errorElement",null);l(this,"initialTokenRequested",!1)}async init(e){this.options&&this.options.partnerKey!==e.partnerKey&&(this.currentToken=null,this.initialTokenRequested=!1),this.options=e,e.token&&(this.currentToken=e.token),this.emit("lifecycle","init",{partnerKey:e.partnerKey,autoMount:e.autoMount!==!1}),e.autoMount!==!1&&await this.mount()}setOptions(e){this.options=e,e.token&&e.token!==this.currentToken&&this.setToken(e.token)}async mount(){let e=this.options;if(!e){this.report("error","not_initialised","FrugaLoader: Cannot mount - init() must be called first");return}if(!this.initialTokenRequested&&e.onTokenRequired&&(this.initialTokenRequested=!0,await this.requestToken(e.onTokenRequired,"initial")),this.session&&this.session.container.isConnected)return;let a=++this.mountGeneration;this.loadFailed=!1,this.storage.remove(ve);let o=be(e.partnerKey,"bootstrap"),r=this.readFreshCache(o);r&&this.startSession(r);try{let u=await this.fetchBootstrap(e.partnerKey,e.baseUrl);if(a!==this.mountGeneration)return;this.storage.set(o,{fetchedAt:Date.now(),bootstrap:u}),this.applyBootstrap(u)}catch(u){if(a!==this.mountGeneration)return;if(r&&this.session){this.report("warn","bootstrap_refresh_failed","FrugaLoader: bootstrap refresh failed, keeping cached config:",u);return}this.failMount(new C("BOOTSTRAP_FAILED",`FrugaLoader: failed to load partner configuration: ${u instanceof Error?u.message:String(u)}`))}}readFreshCache(e){let a=this.storage.get(e);return!a||typeof a.fetchedAt!="number"||!a.bootstrap?null:Date.now()-a.fetchedAt>=Fa?(this.storage.remove(e),null):a.bootstrap}fetchBootstrap(e,a){let o=`${e}::${a??""}`,r=this.inFlightBootstrap;if(r&&r.key===o)return r.promise;let u=this.bootstrapService.bootstrap(e,a),d=()=>{this.inFlightBootstrap?.promise===u&&(this.inFlightBootstrap=null)};return u.then(d,d),this.inFlightBootstrap={key:o,promise:u},u}unmount(){this.mountGeneration+=1;let e=this.session;this.session=null,e&&(this.disposeSession(e),e.ownsContainer&&this.widgetMounter.unmount(e.container),this.emit("lifecycle","unmount",{widgetType:e.widgetType})),this.clearErrorElement(),this.rejectPendingRequests(new Error("FrugaLoader: widget unmounted")),this.initialTokenRequested=!1,this.currentToken=this.options?.token??null}async retry(){this.clearErrorElement(),await this.mount()}requestBalance(){let e=this.session;if(!e){this.report("warn","balance_not_mounted","FrugaLoader: Cannot request balance - widget not mounted");return}let a=this.options?.onBalance;if(!a){this.report("warn","balance_no_callback","FrugaLoader: requestBalance() needs an onBalance option; use getBalance() instead");return}let o=this.nextRequestId(),r=setTimeout(()=>{this.pendingRequests.delete(o)&&this.report("warn","balance_timeout",this.balanceTimeoutMessage(e))},de);this.pendingRequests.set(o,{resolve:u=>a(u),reject:()=>{},timeout:r}),this.sendBalanceRequest(e,o)}async getBalance(){let e=this.session;if(!e)throw new C("NOT_READY","FrugaLoader: Cannot get balance - widget not mounted");let a=this.nextRequestId();return new Promise((o,r)=>{let u=setTimeout(()=>{this.pendingRequests.delete(a)&&r(e.ready?new Error(this.balanceTimeoutMessage(e)):new C("NOT_READY",this.balanceTimeoutMessage(e)))},de);this.pendingRequests.set(a,{resolve:o,reject:r,timeout:u}),this.sendBalanceRequest(e,a)})}sendBalanceRequest(e,a){if(!e.ready){e.queuedBalanceRequests.push(a);return}this.send(e,{type:"FRUGA_GET_BALANCE",version:"1.0",requestId:a})}flushBalanceRequests(e){for(let a of e.queuedBalanceRequests.splice(0))this.pendingRequests.has(a)&&this.sendBalanceRequest(e,a)}balanceTimeoutMessage(e){return e.ready?"FrugaLoader: Get balance request timed out":`FrugaLoader: widget did not report FRUGA_READY within ${de}ms; balance request was never sent`}applyBootstrap(e){let a=this.session,o=ue(e.partnerType);if(!a||a.widgetType!==o){this.startSession(e);return}a.bootstrap=e,a.ready&&this.sendInit(a)}startSession(e){let a=this.options,o=this.documentRef;if(!a||!o||this.loadFailed)return;this.clearErrorElement();let r=this.session;this.session=null,r&&this.disposeSession(r);let{container:u,owned:d}=this.ensureContainer(o,a.containerId||We),s=ue(e.partnerType),{iframe:f,transport:n,cleanup:i}=this.widgetMounter.mount(u,a,s,e.widgetUrl),p={container:u,iframe:f,transport:n,cleanup:i,bootstrap:e,widgetType:s,ready:!1,ownsContainer:d,queuedBalanceRequests:[],loadTimer:null,unsubscribe:()=>{}};p.unsubscribe=n.subscribe(I=>{this.handleMessage(p,I)}),this.session=p,this.startLoadTimer(p,a),this.emit("lifecycle","mount",{widgetType:s})}disposeSession(e){this.clearLoadTimer(e),e.unsubscribe(),e.cleanup(),e.iframe.remove()}ensureContainer(e,a){let o=e.getElementById(a);if(o)return o.innerHTML="",{container:o,owned:o.hasAttribute(Ve)};let r=e.createElement("div");return r.id=a,r.setAttribute(Ve,""),r.style.position="fixed",r.style.bottom="0px",r.style.right="0px",r.style.zIndex="9999",e.body.appendChild(r),{container:r,owned:!0}}async handleMessage(e,a){if(this.session===e)switch(this.emit("protocol",a.type,{direction:"in"}),a.type){case"FRUGA_READY":{if(Y(a.protocolVersion)!==Y("1.0")){this.report("error","protocol_mismatch","FrugaLoader: protocol mismatch",{loader:"1.0",widget:a.protocolVersion}),this.failSession(e,new C("VERSION_MISMATCH",`FrugaLoader: widget protocol ${a.protocolVersion} is incompatible with loader protocol ${"1.0"}`,{widgetVersion:a.widgetVersion,protocolVersion:a.protocolVersion}));return}this.clearLoadTimer(e),this.clearErrorElement(),e.ready=!0,this.emit("lifecycle","ready",{widgetVersion:a.widgetVersion,protocolVersion:a.protocolVersion}),this.sendInit(e),this.flushBalanceRequests(e);return}case"NEED_TOKEN":{await this.refreshToken();return}case"FRUGA_BALANCE_RESPONSE":{this.resolveBalance(a.requestId,a.available,a.pending);return}default:return}}sendInit(e){let a=this.windowRef,o=a?.innerWidth??0,r=a?.innerHeight??0,u=this.buildOverrides();this.send(e,{type:"FRUGA_INIT",version:"1.0",protocolVersion:"1.0",token:this.currentToken??void 0,bootstrap:e.bootstrap,apiBaseUrl:this.options?.baseUrl,host:{origin:a?.location.origin??"",width:o,height:r,isMobile:o<768},...u?{overrides:u}:{}})}buildOverrides(){let e=this.options;if(!e)return;let a={...e.theme?{theme:e.theme}:{},...e.primaryColor?{primaryColor:e.primaryColor}:{},...e.userId?{userId:e.userId}:{},...e.panelOpen?{panelOpen:!0}:{}};return Object.keys(a).length>0?a:void 0}async refreshToken(){let e=this.options?.onTokenRequired;e&&await this.requestToken(e,"ttl")}async requestToken(e,a){this.emit("lifecycle","token_requested",{reason:a});try{this.setToken(await e())}catch(o){this.report("error","token_failed",`FrugaLoader: Failed to fetch ${a} token:`,o)}}setToken(e){this.currentToken=e,this.emit("lifecycle","token_updated",{});let a=this.session;a&&a.ready&&this.send(a,{type:"FRUGA_TOKEN_UPDATE",version:"1.0",token:e})}resolveBalance(e,a,o){if(!e){this.report("warn","balance_response_without_id","FrugaLoader: FRUGA_BALANCE_RESPONSE without requestId ignored");return}let r=this.pendingRequests.get(e);r&&(clearTimeout(r.timeout),this.pendingRequests.delete(e),r.resolve({available:a,pending:o}))}rejectPendingRequests(e){for(let[a,o]of this.pendingRequests)clearTimeout(o.timeout),this.pendingRequests.delete(a),o.reject(e)}nextRequestId(){return Math.random().toString(36).substring(2,15)}startLoadTimer(e,a){let o=a.loadTimeoutMs??He;o<=0||(e.loadTimer=this.setTimer(()=>{e.loadTimer=null,this.failSession(e,new C("TIMEOUT",`FrugaLoader: widget did not report FRUGA_READY within ${o}ms`))},o))}clearLoadTimer(e){e.loadTimer!==null&&(this.clearTimer(e.loadTimer),e.loadTimer=null)}failSession(e,a){this.session===e&&(this.session=null,this.loadFailed=!0,this.disposeSession(e),this.renderErrorElement(e.container),this.rejectPendingRequests(a),this.notifyError(a))}failMount(e){this.loadFailed=!0;let a=this.session;a&&(this.session=null,this.disposeSession(a));let o=this.documentRef,r=this.options?.containerId||We;o&&this.renderErrorElement(this.ensureContainer(o,r).container),this.rejectPendingRequests(e),this.notifyError(e)}notifyError(e){this.emit("error",e.code,{message:e.message});let a=this.options?.onError;if(a)try{a(e)}catch(o){console.error("FrugaLoader: onError callback threw:",o)}}renderErrorElement(e){let a=this.documentRef;if(!a)return;this.clearErrorElement();let o=a.createElement("div");o.setAttribute("role","alert"),o.setAttribute("data-fruga-error",""),o.style.padding="12px",o.textContent=Pa,e.appendChild(o),this.errorElement=o}clearErrorElement(){this.errorElement?.remove(),this.errorElement=null}send(e,a){this.emit("protocol",a.type,{direction:"out"}),e.transport.send(a)}emit(e,a,o,r=e==="error"?"error":"info"){let u=this.options?.onEvent;if(u)try{u({type:e,level:r,name:a,data:o,ts:Date.now()})}catch(d){console.error("FrugaLoader: onEvent callback threw:",d)}}report(e,a,o,r){if(this.options?.onEvent){this.emit("log",a,r===void 0?{message:o}:{message:o,data:r},e);return}let u=e==="error"?console.error:console.warn;if(r===void 0){u(o);return}u(o,r)}setTimer(e,a){let o=this.windowRef;return o?o.setTimeout(e,a):setTimeout(e,a)}clearTimer(e){let a=this.windowRef;if(a){a.clearTimeout(e);return}clearTimeout(e)}get windowRef(){return this.env.windowRef??(typeof window<"u"?window:void 0)}get documentRef(){return this.env.documentRef??(typeof document<"u"?document:void 0)}get storage(){return this.env.storage??Ma}};var Ta=new W,Aa=new V,k=new z(Ta,Aa);var se=async()=>{await k.mount()},q=()=>{k.unmount()},K=async()=>{await k.retry()},X=async t=>(await k.init(t),ya),le=t=>{k.setOptions(t)},fe=()=>{k.requestBalance()},ie=async()=>k.getBalance(),ya={unmount:q,retry:K,getBalance:ie,requestBalance:fe};if(typeof window<"u"){let t=document.currentScript;if(t){let e=t.getAttribute("data-partner-key"),a=t.getAttribute("data-container-id")||void 0,o=t.getAttribute("data-app-url")||void 0,r=t.getAttribute("data-base-url")||void 0,u=window.frugaConfig||{};(e||u.partnerKey)&&X({partnerKey:e||u.partnerKey||"demo-key",containerId:a||u.containerId,appUrl:o||u.appUrl,baseUrl:r||u.baseUrl,theme:u.theme,primaryColor:u.primaryColor})}window.FrugaLoader={init:X,setOptions:le,mount:se,unmount:q,retry:K,requestBalance:fe,getBalance:ie}}import{jsx as Z}from"react/jsx-runtime";var Da=t=>{let e=ne(!1),[a,o]=_e(null),r=ne(t.onError);A(()=>{r.current=t.onError},[t.onError]);let u=Ra(()=>({...t,onError:f=>{o(f),r.current?.(f)}}),[t]),d=ne(u);A(()=>{d.current=u},[u]),A(()=>{e.current&&le(u)},[u]),A(()=>(e.current||X(d.current).then(()=>{e.current=!0}),()=>{q(),e.current=!1}),[t.partnerKey,t.appUrl]);let s=Ba(async()=>{o(null),await K()},[]);return{mount:se,unmount:q,retry:s,error:a}},$r=t=>{let e=t.containerId||"fruga-widget-container";return Da({...t,containerId:e,autoMount:!0}),Z("div",{id:e,style:{width:"100%",height:"100%"}})},va=({children:t,...e})=>{let a=b();return A(()=>{a.send({type:"FRUGA_READY",version:"1.0",protocolVersion:"1.0",widgetVersion:N})},[a]),Z(De,{...e,children:t})},jr=({children:t,...e})=>{let[a,o]=_e(null);return A(()=>{let r=xe();return o(r),()=>{r.dispose()}},[]),a?Z(Be,{transport:a,children:Z(va,{...e,children:t})}):null};export{$r as FrugaWidget,jr as WidgetUI,ie as getBalance,fe as requestBalance,Da as useFrugaWidget};
2
2
  /*! Bundled license information:
3
3
 
4
4
  lucide-react/dist/esm/shared/src/utils/mergeClasses.js:
package/dist/loader.d.mts CHANGED
@@ -1,3 +1,132 @@
1
+ type PartnerType = 'RELAY' | 'CONNECT' | 'EXTERNAL_TENANT';
2
+ interface WidgetUiConfig {
3
+ accent: string;
4
+ defaultTheme: 'light' | 'dark';
5
+ launcherIcon: string;
6
+ showLauncher: boolean;
7
+ launcherLabel: string;
8
+ accentTextDark: string;
9
+ accentTextLight: string;
10
+ allowUserThemeControl: boolean;
11
+ }
12
+ interface BootstrapAuth {
13
+ mode: string;
14
+ audience: string;
15
+ issuer: string;
16
+ tokenTtlSeconds: number;
17
+ }
18
+ /**
19
+ * Who receives the cashback. The wire type is deliberately wider than the two
20
+ * modes the UI knows about: the API may introduce further values, so every
21
+ * consumer narrows through {@link resolvePayoutMode} instead of comparing
22
+ * against a literal.
23
+ */
24
+ type PayoutMode = 'PARTNER' | 'USER';
25
+ /** `partnerPayout` as it arrives on the wire, including values this build does not know. */
26
+ type PartnerPayout = PayoutMode | 'UNKNOWN';
27
+ interface WidgetBootstrap {
28
+ partnerEnvId: string;
29
+ partnerId: string;
30
+ partnerType: PartnerType;
31
+ partnerPayout: PartnerPayout;
32
+ auth: BootstrapAuth;
33
+ config: {
34
+ ui: WidgetUiConfig;
35
+ validValues: {
36
+ defaultTheme: string[];
37
+ launcherIcon: string[];
38
+ };
39
+ };
40
+ /**
41
+ * Absolute URL of the widget document to load. When present the loader uses
42
+ * it instead of its default, which gives ops a kill switch and native
43
+ * wrappers the URL from the same call.
44
+ */
45
+ widgetUrl?: string;
46
+ }
47
+ interface RelayConfig {
48
+ containerId: string;
49
+ partnerKey: string;
50
+ token?: string;
51
+ baseUrl?: string;
52
+ theme?: 'light' | 'dark';
53
+ primaryColor?: string;
54
+ userId?: string;
55
+ bootstrapConfig?: WidgetBootstrap;
56
+ isHostMobile?: boolean;
57
+ hostHeight?: number;
58
+ }
59
+ declare global {
60
+ interface Window {
61
+ FrugaRelay?: {
62
+ init: (config: RelayConfig) => void;
63
+ };
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Why the widget never became usable.
69
+ *
70
+ * - `TIMEOUT`: no FRUGA_READY arrived within `LoaderOptions.loadTimeoutMs`.
71
+ * - `VERSION_MISMATCH`: FRUGA_READY carried an incompatible protocol major.
72
+ * - `BOOTSTRAP_FAILED`: bootstrap could not be resolved and nothing was cached.
73
+ * - `NOT_READY`: a balance request was made before FRUGA_READY and the widget
74
+ * did not become ready in time. Rejects the call only; the mount survives.
75
+ */
76
+ type FrugaLoadErrorCode = 'TIMEOUT' | 'VERSION_MISMATCH' | 'BOOTSTRAP_FAILED' | 'NOT_READY';
77
+ /** Version context attached to a {@link FrugaLoadError}, when known. */
78
+ interface FrugaLoadErrorDetails {
79
+ /** Defaults to the running loader's package version. */
80
+ loaderVersion?: string;
81
+ /** Widget bundle version, as reported in FRUGA_READY. */
82
+ widgetVersion?: string;
83
+ /** Widget protocol version, as reported in FRUGA_READY. */
84
+ protocolVersion?: string;
85
+ }
86
+ /**
87
+ * Host-facing load failure handed to `LoaderOptions.onError`. Carries enough
88
+ * version context for a partner to file a useful bug report.
89
+ */
90
+ declare class FrugaLoadError extends Error {
91
+ readonly code: FrugaLoadErrorCode;
92
+ readonly loaderVersion: string;
93
+ readonly widgetVersion?: string;
94
+ readonly protocolVersion?: string;
95
+ constructor(code: FrugaLoadErrorCode, message: string, details?: FrugaLoadErrorDetails);
96
+ }
97
+
98
+ interface Balance {
99
+ available: number;
100
+ pending: number;
101
+ }
102
+ /**
103
+ * Channel a {@link FrugaLoaderEvent} belongs to: `lifecycle` for mount/ready/
104
+ * token milestones, `protocol` for every postMessage in or out, `log` for
105
+ * anything the loader would otherwise only write to the console, and `error`
106
+ * for a {@link FrugaLoadError} - and only that. The split mirrors the native
107
+ * bridge, where `log` and `error` are separate messages.
108
+ */
109
+ type FrugaLoaderEventType = 'lifecycle' | 'protocol' | 'log' | 'error';
110
+ /**
111
+ * Severity of a {@link FrugaLoaderEvent}. `lifecycle` and `protocol` are always
112
+ * `info`, `error` is always `error`, `log` carries the level of the console call
113
+ * it replaces.
114
+ */
115
+ type FrugaLoaderEventLevel = 'info' | 'warn' | 'error';
116
+ /**
117
+ * Diagnostic event handed to `LoaderOptions.onEvent`. Deliberately flat and
118
+ * JSON-serialisable: the native SDKs forward exactly this shape to their
119
+ * partner log sink, so `name` values are stable API. Never carries a token.
120
+ */
121
+ interface FrugaLoaderEvent {
122
+ type: FrugaLoaderEventType;
123
+ level: FrugaLoaderEventLevel;
124
+ /** Lifecycle milestone, protocol message type, log name, or error code. */
125
+ name: string;
126
+ data?: unknown;
127
+ /** `Date.now()` when the event was emitted. */
128
+ ts: number;
129
+ }
1
130
  interface LoaderOptions {
2
131
  partnerKey: string;
3
132
  containerId?: string;
@@ -9,16 +138,49 @@ interface LoaderOptions {
9
138
  autoMount?: boolean;
10
139
  userId?: string;
11
140
  baseUrl?: string;
141
+ /** Mount with the widget's panel already open. Used by the native shell. */
142
+ panelOpen?: boolean;
143
+ /**
144
+ * Delivers the reply to a fire-and-forget `requestBalance()` call. Without it
145
+ * `requestBalance()` has no consumer and does nothing; use `getBalance()` for
146
+ * the promise-based form.
147
+ */
148
+ onBalance?: (balance: Balance) => void;
149
+ /**
150
+ * How long to wait for the widget's FRUGA_READY before the mount is treated
151
+ * as failed. Defaults to {@link DEFAULT_LOAD_TIMEOUT_MS}; `0` disables the
152
+ * timeout entirely.
153
+ */
154
+ loadTimeoutMs?: number;
155
+ /**
156
+ * Called once per failed mount. The loader has already removed the iframe and
157
+ * rendered its own minimal error element inside the container; call
158
+ * `FrugaHandle.retry()` to try again.
159
+ */
160
+ onError?: (error: FrugaLoadError) => void;
161
+ /**
162
+ * Diagnostic sink: lifecycle milestones, every postMessage in or out, every
163
+ * diagnostic the loader would otherwise log, and every load error. When set it
164
+ * *replaces* the loader's console output; without it the console stays the
165
+ * default sink.
166
+ */
167
+ onEvent?: (event: FrugaLoaderEvent) => void;
168
+ }
169
+ /** What `init()` hands back to the host page. */
170
+ interface FrugaHandle {
171
+ unmount(): void;
172
+ /** Clears any error element and mounts once more. Never retries by itself. */
173
+ retry(): Promise<void>;
174
+ getBalance(): Promise<Balance>;
175
+ requestBalance(): void;
12
176
  }
13
177
 
14
178
  declare const mount: () => Promise<void>;
15
179
  declare const unmount: () => void;
16
- declare const init: (options: LoaderOptions) => Promise<void>;
180
+ declare const retry: () => Promise<void>;
181
+ declare const init: (options: LoaderOptions) => Promise<FrugaHandle>;
17
182
  declare const setOptions: (options: LoaderOptions) => void;
18
183
  declare const requestBalance: () => void;
19
- declare const getBalance: () => Promise<{
20
- available: number;
21
- pending: number;
22
- }>;
184
+ declare const getBalance: () => Promise<Balance>;
23
185
 
24
- export { type LoaderOptions, getBalance, init, mount, requestBalance, setOptions, unmount };
186
+ export { type Balance, type FrugaHandle, FrugaLoadError, type FrugaLoadErrorCode, type FrugaLoadErrorDetails, type FrugaLoaderEvent, type FrugaLoaderEventLevel, type FrugaLoaderEventType, type LoaderOptions, getBalance, init, mount, requestBalance, retry, setOptions, unmount };
package/dist/loader.d.ts CHANGED
@@ -1,3 +1,132 @@
1
+ type PartnerType = 'RELAY' | 'CONNECT' | 'EXTERNAL_TENANT';
2
+ interface WidgetUiConfig {
3
+ accent: string;
4
+ defaultTheme: 'light' | 'dark';
5
+ launcherIcon: string;
6
+ showLauncher: boolean;
7
+ launcherLabel: string;
8
+ accentTextDark: string;
9
+ accentTextLight: string;
10
+ allowUserThemeControl: boolean;
11
+ }
12
+ interface BootstrapAuth {
13
+ mode: string;
14
+ audience: string;
15
+ issuer: string;
16
+ tokenTtlSeconds: number;
17
+ }
18
+ /**
19
+ * Who receives the cashback. The wire type is deliberately wider than the two
20
+ * modes the UI knows about: the API may introduce further values, so every
21
+ * consumer narrows through {@link resolvePayoutMode} instead of comparing
22
+ * against a literal.
23
+ */
24
+ type PayoutMode = 'PARTNER' | 'USER';
25
+ /** `partnerPayout` as it arrives on the wire, including values this build does not know. */
26
+ type PartnerPayout = PayoutMode | 'UNKNOWN';
27
+ interface WidgetBootstrap {
28
+ partnerEnvId: string;
29
+ partnerId: string;
30
+ partnerType: PartnerType;
31
+ partnerPayout: PartnerPayout;
32
+ auth: BootstrapAuth;
33
+ config: {
34
+ ui: WidgetUiConfig;
35
+ validValues: {
36
+ defaultTheme: string[];
37
+ launcherIcon: string[];
38
+ };
39
+ };
40
+ /**
41
+ * Absolute URL of the widget document to load. When present the loader uses
42
+ * it instead of its default, which gives ops a kill switch and native
43
+ * wrappers the URL from the same call.
44
+ */
45
+ widgetUrl?: string;
46
+ }
47
+ interface RelayConfig {
48
+ containerId: string;
49
+ partnerKey: string;
50
+ token?: string;
51
+ baseUrl?: string;
52
+ theme?: 'light' | 'dark';
53
+ primaryColor?: string;
54
+ userId?: string;
55
+ bootstrapConfig?: WidgetBootstrap;
56
+ isHostMobile?: boolean;
57
+ hostHeight?: number;
58
+ }
59
+ declare global {
60
+ interface Window {
61
+ FrugaRelay?: {
62
+ init: (config: RelayConfig) => void;
63
+ };
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Why the widget never became usable.
69
+ *
70
+ * - `TIMEOUT`: no FRUGA_READY arrived within `LoaderOptions.loadTimeoutMs`.
71
+ * - `VERSION_MISMATCH`: FRUGA_READY carried an incompatible protocol major.
72
+ * - `BOOTSTRAP_FAILED`: bootstrap could not be resolved and nothing was cached.
73
+ * - `NOT_READY`: a balance request was made before FRUGA_READY and the widget
74
+ * did not become ready in time. Rejects the call only; the mount survives.
75
+ */
76
+ type FrugaLoadErrorCode = 'TIMEOUT' | 'VERSION_MISMATCH' | 'BOOTSTRAP_FAILED' | 'NOT_READY';
77
+ /** Version context attached to a {@link FrugaLoadError}, when known. */
78
+ interface FrugaLoadErrorDetails {
79
+ /** Defaults to the running loader's package version. */
80
+ loaderVersion?: string;
81
+ /** Widget bundle version, as reported in FRUGA_READY. */
82
+ widgetVersion?: string;
83
+ /** Widget protocol version, as reported in FRUGA_READY. */
84
+ protocolVersion?: string;
85
+ }
86
+ /**
87
+ * Host-facing load failure handed to `LoaderOptions.onError`. Carries enough
88
+ * version context for a partner to file a useful bug report.
89
+ */
90
+ declare class FrugaLoadError extends Error {
91
+ readonly code: FrugaLoadErrorCode;
92
+ readonly loaderVersion: string;
93
+ readonly widgetVersion?: string;
94
+ readonly protocolVersion?: string;
95
+ constructor(code: FrugaLoadErrorCode, message: string, details?: FrugaLoadErrorDetails);
96
+ }
97
+
98
+ interface Balance {
99
+ available: number;
100
+ pending: number;
101
+ }
102
+ /**
103
+ * Channel a {@link FrugaLoaderEvent} belongs to: `lifecycle` for mount/ready/
104
+ * token milestones, `protocol` for every postMessage in or out, `log` for
105
+ * anything the loader would otherwise only write to the console, and `error`
106
+ * for a {@link FrugaLoadError} - and only that. The split mirrors the native
107
+ * bridge, where `log` and `error` are separate messages.
108
+ */
109
+ type FrugaLoaderEventType = 'lifecycle' | 'protocol' | 'log' | 'error';
110
+ /**
111
+ * Severity of a {@link FrugaLoaderEvent}. `lifecycle` and `protocol` are always
112
+ * `info`, `error` is always `error`, `log` carries the level of the console call
113
+ * it replaces.
114
+ */
115
+ type FrugaLoaderEventLevel = 'info' | 'warn' | 'error';
116
+ /**
117
+ * Diagnostic event handed to `LoaderOptions.onEvent`. Deliberately flat and
118
+ * JSON-serialisable: the native SDKs forward exactly this shape to their
119
+ * partner log sink, so `name` values are stable API. Never carries a token.
120
+ */
121
+ interface FrugaLoaderEvent {
122
+ type: FrugaLoaderEventType;
123
+ level: FrugaLoaderEventLevel;
124
+ /** Lifecycle milestone, protocol message type, log name, or error code. */
125
+ name: string;
126
+ data?: unknown;
127
+ /** `Date.now()` when the event was emitted. */
128
+ ts: number;
129
+ }
1
130
  interface LoaderOptions {
2
131
  partnerKey: string;
3
132
  containerId?: string;
@@ -9,16 +138,49 @@ interface LoaderOptions {
9
138
  autoMount?: boolean;
10
139
  userId?: string;
11
140
  baseUrl?: string;
141
+ /** Mount with the widget's panel already open. Used by the native shell. */
142
+ panelOpen?: boolean;
143
+ /**
144
+ * Delivers the reply to a fire-and-forget `requestBalance()` call. Without it
145
+ * `requestBalance()` has no consumer and does nothing; use `getBalance()` for
146
+ * the promise-based form.
147
+ */
148
+ onBalance?: (balance: Balance) => void;
149
+ /**
150
+ * How long to wait for the widget's FRUGA_READY before the mount is treated
151
+ * as failed. Defaults to {@link DEFAULT_LOAD_TIMEOUT_MS}; `0` disables the
152
+ * timeout entirely.
153
+ */
154
+ loadTimeoutMs?: number;
155
+ /**
156
+ * Called once per failed mount. The loader has already removed the iframe and
157
+ * rendered its own minimal error element inside the container; call
158
+ * `FrugaHandle.retry()` to try again.
159
+ */
160
+ onError?: (error: FrugaLoadError) => void;
161
+ /**
162
+ * Diagnostic sink: lifecycle milestones, every postMessage in or out, every
163
+ * diagnostic the loader would otherwise log, and every load error. When set it
164
+ * *replaces* the loader's console output; without it the console stays the
165
+ * default sink.
166
+ */
167
+ onEvent?: (event: FrugaLoaderEvent) => void;
168
+ }
169
+ /** What `init()` hands back to the host page. */
170
+ interface FrugaHandle {
171
+ unmount(): void;
172
+ /** Clears any error element and mounts once more. Never retries by itself. */
173
+ retry(): Promise<void>;
174
+ getBalance(): Promise<Balance>;
175
+ requestBalance(): void;
12
176
  }
13
177
 
14
178
  declare const mount: () => Promise<void>;
15
179
  declare const unmount: () => void;
16
- declare const init: (options: LoaderOptions) => Promise<void>;
180
+ declare const retry: () => Promise<void>;
181
+ declare const init: (options: LoaderOptions) => Promise<FrugaHandle>;
17
182
  declare const setOptions: (options: LoaderOptions) => void;
18
183
  declare const requestBalance: () => void;
19
- declare const getBalance: () => Promise<{
20
- available: number;
21
- pending: number;
22
- }>;
184
+ declare const getBalance: () => Promise<Balance>;
23
185
 
24
- export { type LoaderOptions, getBalance, init, mount, requestBalance, setOptions, unmount };
186
+ export { type Balance, type FrugaHandle, FrugaLoadError, type FrugaLoadErrorCode, type FrugaLoadErrorDetails, type FrugaLoaderEvent, type FrugaLoaderEventLevel, type FrugaLoaderEventType, type LoaderOptions, getBalance, init, mount, requestBalance, retry, setOptions, unmount };