@stacknet/userutils 0.6.10 → 0.6.11

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,2 +1,2 @@
1
- 'use strict';var react=require('react');function ce(){if(typeof document>"u")return null;try{let e=document.cookie.split(";").map(i=>i.trim()).find(i=>i.startsWith("stackauth_session="));if(!e)return null;let n=e.slice(18);return JSON.parse(atob(n.replace(/-/g,"+").replace(/_/g,"/")))}catch{return null}}function x(e="__csrf"){if(typeof document>"u")return null;let n=document.cookie.split(";").map(i=>i.trim()).find(i=>i.startsWith(`${e}=`));return n?n.slice(e.length+1):null}function L(){let[e,n]=react.useState(null),[i,l]=react.useState(true),d=react.useCallback(()=>{let o=ce();o&&o.expiresAt>Date.now()?n({userId:o.userId,address:o.address,chain:o.chain,expiresAt:o.expiresAt,planId:o.planId,authMethod:o.authMethod}):n(null),l(false);},[]);react.useEffect(()=>{d();},[d]);let g=react.useCallback(async(o="")=>{try{let s=await fetch(`${o}/api/auth/session`);if(s.ok){let a=await s.json();if(a.session)return n(a.session),a.session}return n(null),null}catch{return null}},[]),f=!!e&&e.expiresAt>Date.now();return {session:e,loading:i,isAuthenticated:f,refresh:g,readSession:d}}function K(e="__csrf",n="x-csrf-token"){let[i,l]=react.useState(null);react.useEffect(()=>{l(x(e));},[e]);let d=i?{[n]:i}:{};return {token:i,headers:d}}function V(){let[e,n]=react.useState({connected:false,address:null,chain:null,provider:null}),[i,l]=react.useState(null),d=react.useCallback(async(s="phantom")=>{l(null);try{let a=typeof window<"u"?window:null,c=s==="phantom"?a?.phantom?.solana||a?.solana:a?.solflare;if(!c)return l(`${s} wallet not found`),null;let m=(await c.connect()).publicKey.toString();return n({connected:!0,address:m,chain:"solana",provider:s}),m}catch(a){return l(a.message||"Failed to connect wallet"),null}},[]),g=react.useCallback(async()=>{l(null);try{let a=(typeof window<"u"?window:null)?.ethereum;if(!a)return l("MetaMask not found"),null;let c=a;a.providers?.length&&(c=a.providers.find(t=>t.isMetaMask)||a);let m=(await c.request({method:"eth_requestAccounts"}))[0];return m?(n({connected:!0,address:m,chain:"ethereum",provider:"metamask"}),m):(l("No account selected"),null)}catch(s){return l(s.message||"Failed to connect wallet"),null}},[]),f=react.useCallback(async(s,a)=>{l(null);let c=a?.chain||e.chain,u=a?.provider||e.provider,m=a?.address||e.address;try{if(c==="solana"){let t=typeof window<"u"?window:null,p=u==="solflare"?t?.solflare:t?.phantom?.solana||t?.solana;if(!p)throw new Error("Wallet not available");let r=new TextEncoder().encode(s),h=await p.signMessage(r,"utf8"),y=new Uint8Array(h.signature||h),w="";for(let A=0;A<y.byteLength;A++)w+=String.fromCharCode(y[A]);return btoa(w)}if(c==="ethereum"){let p=(typeof window<"u"?window:null)?.ethereum;if(p?.providers?.length&&(p=p.providers.find(h=>h.isMetaMask)||p),!p)throw new Error("MetaMask not available");return await p.request({method:"personal_sign",params:[s,m]})}throw new Error("No wallet connected")}catch(t){return l(t.message||"Signing failed"),null}},[e]),o=react.useCallback(()=>{n({connected:false,address:null,chain:null,provider:null}),l(null);},[]);return {wallet:e,error:i,connectSolana:d,connectEVM:g,signMessage:f,disconnect:o}}var Ae="https://stacknet.magma-rpc.com/auth/bridge",D="stacknet-auth-bridge";function q(e){let n=e?.bridgeUrl||Ae,i=e?.disabled||false,l=react.useRef(null),[d,g]=react.useState({ready:false,known:false,identity:null,identityCount:0,resolvedStackId:null}),f=react.useRef([]),o=react.useRef(false),s=react.useCallback(t=>{let p={...t,protocol:D};o.current&&l.current?.contentWindow?l.current.contentWindow.postMessage(p,new URL(n).origin):f.current.push(p);},[n]);react.useEffect(()=>{if(i)return;let t=r=>{if(!(!r.data||r.data.protocol!==D)){try{if(r.origin!==new URL(n).origin)return}catch{return}switch(r.data.type){case "bridge:ready":o.current=true,g(h=>({...h,ready:true}));for(let h of f.current)l.current?.contentWindow?.postMessage(h,r.origin);f.current=[],l.current?.contentWindow?.postMessage({protocol:D,type:"auth:check"},r.origin),l.current?.contentWindow?.postMessage({protocol:D,type:"auth:resolve-stack"},r.origin);break;case "auth:status":g(h=>({...h,known:r.data.known,identity:r.data.identity,identityCount:r.data.identityCount||0}));break;case "auth:resolved-stack":g(h=>({...h,resolvedStackId:r.data.stackId||null}));break;}}};window.addEventListener("message",t);let p=document.createElement("iframe");return p.src=n,p.style.display="none",p.setAttribute("aria-hidden","true"),p.setAttribute("tabindex","-1"),p.setAttribute("sandbox","allow-scripts allow-same-origin"),document.body.appendChild(p),l.current=p,()=>{window.removeEventListener("message",t),p.parentNode&&p.parentNode.removeChild(p),l.current=null,o.current=false;}},[n,i]);let a=react.useCallback(t=>{s({type:"auth:connected",...t});},[s]),c=react.useCallback(t=>{s({type:"auth:disconnected",...t});},[s]),u=react.useCallback(()=>{s({type:"auth:clear"}),g({ready:d.ready,known:false,identity:null,identityCount:0,resolvedStackId:null});},[s,d.ready]),m=react.useCallback(()=>{s({type:"auth:check"});},[s]);return {...d,reportConnected:a,reportDisconnected:c,clearAll:u,refresh:m}}async function ge(e,n,i,l){let d=e.apiVersion||"v2",g=`${e.baseUrl}/api/${d}${i}`;try{let f=await fetch(g,{method:n,headers:{"Content-Type":"application/json"},body:l?JSON.stringify(l):void 0}),o=await f.json();return f.ok?o.success&&o.data!==void 0?{success:!0,data:o.data}:{success:!0,data:o}:{success:!1,error:o.error||{code:"UNKNOWN_ERROR",message:"Unknown error"}}}catch(f){return {success:false,error:{code:"NETWORK_ERROR",message:f instanceof Error?f.message:"Network error"}}}}function pe(e){return {getNetworkStatus:()=>ge(e,"GET","/network/status"),getWeb3Challenge:(n,i)=>ge(e,"POST",`/stacks/${encodeURIComponent(e.stackId)}/auth/web3/challenge`,{chain:n,address:i})}}function Te(e={apiBaseUrl:""}){let{wallet:n,connectSolana:i,connectEVM:l,signMessage:d,disconnect:g}=V(),{session:f,isAuthenticated:o,refresh:s,readSession:a}=L(),{headers:c}=K(),u=q({disabled:typeof window>"u"}),[m,t]=react.useState(false),[p,r]=react.useState(null),[h,y]=react.useState(false),w=e.apiBaseUrl||"",A=e.stacknetUrl||"https://stacknet.magma-rpc.com",C=e.stackId||u.resolvedStackId||"",S=pe({baseUrl:A,stackId:C}),E=react.useCallback(async(k,P,O,R)=>{t(true),r(null);try{let v=P;if(!v){let B=await O();if(!B)return t(!1),!1;v=B;}let T=await S.getWeb3Challenge(k,v);if(!T.success||!T.data)return r("Failed to get challenge"),t(!1),!1;let U=await d(T.data.message,{chain:k,provider:R,address:v});if(!U)return t(!1),!1;let G={chain:k,message:T.data.message,signature:U,stackId:C};k==="solana"&&(G.publicKey=v);let J=await fetch(`${w}/api/auth/callback`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify(G)});if(!J.ok){let B=await J.json().catch(()=>({}));return r(B.error||"Authentication failed"),t(!1),!1}return u.reportConnected({address:v,chain:k,method:R||(k==="solana"?"phantom":"metamask"),stackId:C}),a(),t(!1),!0}catch(v){return r(v.message||"Authentication failed"),t(false),false}},[w,S,d,a,u,C]),$=react.useCallback(async(k="phantom")=>{let P=await i(k);return P?E("solana",P,()=>i(k),k):false},[i,E]),W=react.useCallback(async()=>{let k=await l();return k?E("ethereum",k,l,"metamask"):false},[l,E]),b=react.useCallback(async k=>{t(true),r(null);try{let P=await fetch(`${w}/api/auth/otp`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify({code:k})});if(!P.ok){let O=await P.json().catch(()=>({}));return r(O.error||"Invalid code"),t(!1),!1}return a(),t(!1),!0}catch(P){return r(P.message||"OTP verification failed"),t(false),false}},[w,a]),I=react.useCallback(async(k,P)=>{t(true),r(null);try{let O=P||`${window.location.origin}/api/auth/oauth/callback`,R=new URLSearchParams({provider:k,redirectUri:O,stackId:C}),v=await fetch(`${w}/api/auth/oauth/start?${R}`,{credentials:"include"});if(!v.ok){let U=await v.json().catch(()=>({}));return r(U.error||"Failed to start OAuth flow"),t(!1),!1}let T=await v.json();if(T.redirect_url){let U;try{U=new URL(T.redirect_url);}catch{return r("Invalid OAuth redirect URL"),t(!1),!1}let G=[/(^|\.)accounts\.google\.com$/,/(^|\.)discord\.com$/,/(^|\.)github\.com$/,/(^|\.)x\.com$/,/(^|\.)twitter\.com$/,/(^|\.)apple\.com$/];return U.protocol!=="https:"||!G.some(J=>J.test(U.hostname))?(r(`Refusing to redirect to non-OAuth host: ${U.hostname}`),t(!1),!1):(typeof sessionStorage<"u"&&(sessionStorage.setItem("oauth_state",T.state||""),sessionStorage.setItem("oauth_provider",k)),window.location.href=U.toString(),!0)}return r("No redirect URL returned"),t(!1),!1}catch(O){return r(O.message||"OAuth flow failed"),t(false),false}},[w,C]),N=react.useCallback(async(k,P,O)=>{t(true),r(null);try{if(typeof sessionStorage<"u"){let v=sessionStorage.getItem("oauth_state"),T=sessionStorage.getItem("oauth_provider");if(sessionStorage.removeItem("oauth_state"),sessionStorage.removeItem("oauth_provider"),!v||v!==O)return r("OAuth state mismatch \u2014 refusing to complete login"),t(!1),!1;if(T&&T!==k)return r("OAuth provider mismatch \u2014 refusing to complete login"),t(!1),!1}let R=await fetch(`${w}/api/auth/oauth/callback`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify({provider:k,code:P,state:O,stackId:C})});if(!R.ok){let v=await R.json().catch(()=>({}));return r(v.error||"OAuth authentication failed"),t(!1),!1}return a(),t(!1),!0}catch(R){return r(R.message||"OAuth callback failed"),t(false),false}},[w,C,a]),j=react.useCallback(async()=>{n.address&&n.chain&&u.reportDisconnected({address:n.address,chain:n.chain,stackId:C});try{await fetch(`${w}/api/auth/logout`,{method:"POST",headers:c,credentials:"include"});}catch{}g(),a();},[w,c,g,a,n,u,C]);return react.useEffect(()=>{if(!e.autoConnect||h||o||!u.ready||!u.known||!u.identity)return;y(true);let{chain:k,method:P}=u.identity;k==="solana"&&(P==="phantom"||P==="solflare")?$(P):k==="ethereum"&&W();},[e.autoConnect,h,o,u,$,W]),{session:f,isAuthenticated:o,wallet:n,loading:m,error:p,authenticateSolana:$,authenticateEVM:W,authenticateOTP:b,authenticateOAuth:I,authenticateOAuthCallback:N,logout:j,refresh:()=>s(w),stackId:C,bridge:{ready:u.ready,known:u.known,identity:u.identity,identityCount:u.identityCount,resolvedStackId:u.resolvedStackId}}}function Y(e,n="https://stacknet.magma-rpc.com"){let[i,l]=react.useState(null),[d,g]=react.useState(false),[f,o]=react.useState(null),s=react.useCallback(async c=>{g(true),o(null);try{let u=await fetch(`${n}/api/v2/stacks/${c}`);if(!u.ok)return o("Stack not found"),g(!1),null;let m=await u.json(),t=m.data?.stack||m.stack||m,p={id:t.id,name:t.name,displayName:t.displayName||t.name,description:t.description,logoUrl:t.logoUrl,webPageUrl:t.webPageUrl,allowedChains:t.allowedChains||[],features:t.features,stripeProvider:t.stripeProvider,oauthProviders:t.oauthProviders?.map(r=>({provider:r.provider,clientId:r.clientId,enabled:r.enabled!==!1}))};return l(p),g(!1),p}catch(u){return o(u.message),g(false),null}},[n]);react.useEffect(()=>{e&&s(e);},[e,s]);let a=i?Re(i):[];return {config:i,loading:d,error:f,identityProviders:a,fetchConfig:s}}function Re(e){let n=[];if(e.features?.web3Auth!==false&&(e.allowedChains.includes("solana")&&(n.push({type:"wallet",id:"phantom",name:"Phantom",chain:"solana"}),n.push({type:"wallet",id:"solflare",name:"Solflare",chain:"solana"})),(e.allowedChains.includes("ethereum")||e.allowedChains.includes("polygon")||e.allowedChains.includes("base"))&&n.push({type:"wallet",id:"metamask",name:"MetaMask",chain:"ethereum"})),e.features?.apiKeyAuth!==false&&n.push({type:"otp",id:"otp",name:"Access Code"}),e.features?.oauthAuth&&e.oauthProviders)for(let i of e.oauthProviders)i.enabled&&n.push({type:"oauth",id:i.provider,name:i.provider});return n}function _e(e=""){let[n,i]=react.useState([]),[l,d]=react.useState(true),[g,f]=react.useState(null),o=react.useCallback(async()=>{try{let s=await fetch(`${e}/api/billing/plans`);if(s.ok){let a=await s.json();i(a.plans||a||[]);}}catch(s){f(s.message);}finally{d(false);}},[e]);return react.useEffect(()=>{o();},[o]),{plans:n,loading:l,error:g,refresh:o}}function We(e=""){let[n,i]=react.useState(null),[l,d]=react.useState(true),[g,f]=react.useState(null),o=react.useCallback(async()=>{try{let c=await fetch(`${e}/api/billing/subscription`);if(c.ok){let u=await c.json();i(u.plan?u:null);}}catch(c){f(c.message);}finally{d(false);}},[e]);react.useEffect(()=>{o();},[o]);let s=react.useCallback(async c=>{let u=x(),t=await(await fetch(`${e}/api/billing/subscribe`,{method:"POST",headers:{"Content-Type":"application/json",...u?{"x-csrf-token":u}:{}},body:JSON.stringify({planId:c})})).json();return t.url||t.checkoutUrl||null},[e]),a=react.useCallback(async()=>{let c=x();return (await fetch(`${e}/api/billing/cancel`,{method:"POST",headers:c?{"x-csrf-token":c}:{}})).ok?(await o(),true):false},[e,o]);return {subscription:n,loading:l,error:g,refresh:o,subscribe:s,cancel:a}}function Me(e=""){let[n,i]=react.useState(null),[l,d]=react.useState(true),[g,f]=react.useState(null),o=react.useCallback(async()=>{try{let s=await fetch(`${e}/api/billing/usage`);if(s.ok){let a=await s.json();i(a);}}catch(s){f(s.message);}finally{d(false);}},[e]);return react.useEffect(()=>{o();},[o]),{usage:n,loading:l,error:g,refresh:o}}function je(e=""){let[n,i]=react.useState(false),[l,d]=react.useState(null),g=react.useCallback(async o=>{i(true),d(null);try{let s=x(),a=await fetch(`${e}/api/billing/prepaid`,{method:"POST",headers:{"Content-Type":"application/json",...s?{"x-csrf-token":s}:{}},body:JSON.stringify({amountCents:o})}),c=await a.json();return a.ok?c.url||null:(d(c.error||"Purchase failed"),null)}catch(s){return d(s.message),null}finally{i(false);}},[e]),f=react.useCallback(async o=>{i(true),d(null);try{let s=x(),a=await fetch(`${e}/api/billing/verify-prepaid`,{method:"POST",headers:{"Content-Type":"application/json",...s?{"x-csrf-token":s}:{}},body:JSON.stringify({sessionId:o})}),c=await a.json();return a.ok?c:(d(c.error||"Verification failed"),null)}catch(s){return d(s.message),null}finally{i(false);}},[e]);return {purchase:g,verifySession:f,loading:n,error:l}}function Be(e="",n){let[i,l]=react.useState([]),[d,g]=react.useState(true),[f,o]=react.useState(null),s=n?.limit||50,a=n?.offset||0,c=react.useCallback(async()=>{try{let u=await fetch(`${e}/api/billing/history?limit=${s}&offset=${a}`);if(u.ok){let m=await u.json();l(m.records||m.history||(Array.isArray(m)?m:[]));}}catch(u){o(u.message);}finally{g(false);}},[e,s,a]);return react.useEffect(()=>{c();},[c]),{records:i,loading:d,error:f,refresh:c}}function De(){if(typeof document>"u")return null;let e=document.cookie.split(";").map(n=>n.trim()).find(n=>n.startsWith("__csrf="));return e?e.slice(7):null}function He(e,n){let[i,l]=react.useState(null),[d,g]=react.useState(true),[f,o]=react.useState(false),[s,a]=react.useState(null),c=n?.apiBaseUrl??"",u=n?.scope===void 0||n?.scope==="global"?"global":`stack:${n.scope.stackId}`,m=react.useCallback(r=>{let h=encodeURIComponent(r);if(u==="global")return `${c}/api/user/profile/${h}`;let y=u.slice(6);return `${c}/api/v2/stacks/${encodeURIComponent(y)}/members/${h}/profile`},[c,u]),t=react.useCallback(async()=>{if(!e){l(null),g(false);return}g(true),a(null);try{let r=await fetch(m(e));if(r.ok){let h=await r.json(),y=h.profile||h.data?.profile||h;l({mid:y.mid||e,username:y.username||"",avatarUrl:y.avatar_url||y.avatarUrl,bio:y.bio,paymentAddress:y.payment_address||y.paymentAddress,createdAt:y.created_at||y.createdAt,updatedAt:y.updated_at||y.updatedAt});}else if(r.status===404)l({mid:e,username:""});else throw new Error(`${r.status}`)}catch(r){a(r instanceof Error?r.message:"Failed to load profile");}finally{g(false);}},[e,m]);react.useEffect(()=>{t();},[t]);let p=react.useCallback(async r=>{if(!e)return false;o(true),a(null);try{let h=De(),y={};r.username!==void 0&&(y.username=r.username),r.avatarUrl!==void 0&&(y.avatar_url=r.avatarUrl),r.bio!==void 0&&(y.bio=r.bio),r.paymentAddress!==void 0&&(y.payment_address=r.paymentAddress);let w=await fetch(m(e),{method:"PUT",headers:{"Content-Type":"application/json",...h?{"x-csrf-token":h}:{}},credentials:"same-origin",body:JSON.stringify(y)});if(!w.ok){let S=await w.json().catch(()=>({}));throw new Error(S.error||S.message||`Update failed: ${w.status}`)}let A=await w.json(),C=A.profile||A.data?.profile||A;return l(S=>({mid:S?.mid||e,username:r.username??S?.username??"",avatarUrl:r.avatarUrl??S?.avatarUrl,bio:r.bio??S?.bio,paymentAddress:r.paymentAddress??C.payment_address??C.paymentAddress??S?.paymentAddress,createdAt:S?.createdAt,updatedAt:C.updated_at||C.updatedAt||Date.now()})),!0}catch(h){return a(h instanceof Error?h.message:"Update failed"),false}finally{o(false);}},[e,m]);return {profile:i,loading:d,saving:f,error:s,updateProfile:p,refresh:t}}var be="google-identity-services",Ke="https://accounts.google.com/gsi/client";function Ve({stackId:e,stacknetUrl:n="https://stacknet.magma-rpc.com",apiBaseUrl:i="",autoPrompt:l=true,cancelOnTapOutside:d=true,onSuccess:g,onError:f,disabled:o=false}){let{config:s}=Y(e,n),{isAuthenticated:a,loading:c,readSession:u}=L(),[m,t]=react.useState(false),[p,r]=react.useState(null),[h,y]=react.useState(false),w=react.useRef(false),A=react.useRef(false),S=s?.oauthProviders?.find(b=>b.provider==="google"&&b.enabled&&b.clientId)?.clientId||null;react.useEffect(()=>{if(o||!S||typeof window>"u")return;if(document.getElementById(be)){y(true);return}let b=document.createElement("script");b.id=be,b.src=Ke,b.async=true,b.defer=true,b.onload=()=>y(true),b.onerror=()=>{r("Failed to load Google sign-in"),f?.("Failed to load Google Identity Services script");},document.head.appendChild(b);},[o,S,f]);let E=react.useCallback(async b=>{t(true),r(null);try{let I=await fetch(`${i}/api/auth/google/one-tap`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({credential:b.credential,stackId:e})});if(!I.ok){let j=(await I.json().catch(()=>({}))).error||"Google sign-in failed";r(j),f?.(j),t(!1);return}u(),t(!1),g?.();}catch(I){let N=I.message||"Google sign-in failed";r(N),f?.(N),t(false);}},[i,e,u,g,f]);react.useEffect(()=>{if(o){console.debug("[GoogleOneTap] Disabled");return}if(!h){console.debug("[GoogleOneTap] Script not loaded yet, clientId:",S);return}if(!S){console.debug("[GoogleOneTap] No Google clientId from stack config");return}if(c){console.debug("[GoogleOneTap] Session still loading");return}if(a){console.debug("[GoogleOneTap] User already authenticated, skipping");return}if(!window.google?.accounts?.id){console.debug("[GoogleOneTap] GIS library not available on window");return}w.current||(console.debug("[GoogleOneTap] Initializing with clientId:",S),w.current=true,window.google.accounts.id.initialize({client_id:S,callback:E,auto_select:true,cancel_on_tap_outside:d}),l&&!A.current&&(A.current=true,console.debug("[GoogleOneTap] Showing prompt..."),window.google.accounts.id.prompt(b=>{b.isDisplayed?.()&&console.debug("[GoogleOneTap] Prompt displayed"),b.isNotDisplayed?.()&&console.debug("[GoogleOneTap] Not displayed:",b.getNotDisplayedReason?.()),b.isSkippedMoment?.()&&console.debug("[GoogleOneTap] Skipped:",b.getSkippedReason?.()),b.isDismissedMoment?.()&&console.debug("[GoogleOneTap] Dismissed:",b.getDismissedReason?.());})));},[o,h,S,c,a,E,l,d]),react.useEffect(()=>()=>{window.google?.accounts?.id&&w.current&&window.google.accounts.id.cancel();},[]);let $=react.useCallback(()=>{!window.google?.accounts?.id||!w.current||window.google.accounts.id.prompt();},[]),W=react.useCallback((b,I)=>{!b||!window.google?.accounts?.id||!w.current||window.google.accounts.id.renderButton(b,{theme:I?.theme||"filled_black",size:I?.size||"large",text:I?.text||"signin_with",width:I?.width});},[]);return {available:!!S,ready:h&&!!S,loading:m,error:p,prompt:$,renderButton:W,clientId:S}}var qe="/api";function Qe(e,n){return `${(n||(typeof window<"u"?window.location.origin:"")).replace(/\/$/,"")}/?ref=${encodeURIComponent(e)}`}function Xe(e={}){let{shareBaseUrl:n,autoMint:i=true}=e,[l,d]=react.useState(null),[g,f]=react.useState(true),[o,s]=react.useState(null),a=react.useCallback(async()=>{f(true),s(null);try{let m=i?"POST":"GET",t=await fetch(`${qe}/social/join-code`,{method:m,credentials:"include"});if(!t.ok){d(null),t.status===401?s("not_authenticated"):s(`HTTP ${t.status}`);return}let p=await t.json();d(p?.code??null);}catch(m){s(m?.message||"network_error"),d(null);}finally{f(false);}},[i]);react.useEffect(()=>{a();},[a]);let c=l?Qe(l,n):null,u=react.useCallback(async()=>{if(!c)return false;try{if(typeof navigator<"u"&&navigator.clipboard?.writeText)return await navigator.clipboard.writeText(c),!0}catch{}return false},[c]);return {code:l,shareUrl:c,loading:g,error:o,refresh:a,copyShareLink:u}}
2
- exports.useAuthBridge=q;exports.useBillingHistory=Be;exports.useCSRFToken=K;exports.useGoogleOneTap=Ve;exports.useJoinCode=Xe;exports.usePlans=_e;exports.usePrepaidCheckout=je;exports.useProfile=He;exports.useSession=L;exports.useStackAuth=Te;exports.useStackConfig=Y;exports.useSubscription=We;exports.useUsage=Me;exports.useWeb3Wallet=V;
1
+ 'use strict';var react=require('react');function de(){if(typeof document>"u")return null;try{let e=document.cookie.split(";").map(i=>i.trim()).find(i=>i.startsWith("stackauth_session="));if(!e)return null;let n=e.slice(18);return JSON.parse(atob(n.replace(/-/g,"+").replace(/_/g,"/")))}catch{return null}}function O(e="__csrf"){if(typeof document>"u")return null;let n=document.cookie.split(";").map(i=>i.trim()).find(i=>i.startsWith(`${e}=`));return n?n.slice(e.length+1):null}function W(){let[e,n]=react.useState(null),[i,l]=react.useState(true),d=react.useCallback(()=>{let s=de();s&&s.expiresAt>Date.now()?n({userId:s.userId,address:s.address,chain:s.chain,expiresAt:s.expiresAt,planId:s.planId,authMethod:s.authMethod}):n(null),l(false);},[]);react.useEffect(()=>{d();},[d]);let p=react.useCallback(async(s="")=>{try{let o=await fetch(`${s}/api/auth/session`);if(o.ok){let a=await o.json();if(a.session)return n(a.session),a.session}return n(null),null}catch{return null}},[]),m=!!e&&e.expiresAt>Date.now();return {session:e,loading:i,isAuthenticated:m,refresh:p,readSession:d}}function z(e="__csrf",n="x-csrf-token"){let[i,l]=react.useState(null);react.useEffect(()=>{l(O(e));},[e]);let d=i?{[n]:i}:{};return {token:i,headers:d}}function q(){let[e,n]=react.useState({connected:false,address:null,chain:null,provider:null}),[i,l]=react.useState(null),d=react.useCallback(async(o="phantom")=>{l(null);try{let a=typeof window<"u"?window:null,u=o==="phantom"?a?.phantom?.solana||a?.solana:a?.solflare;if(!u)return l(`${o} wallet not found`),null;let y=(await u.connect()).publicKey.toString();return n({connected:!0,address:y,chain:"solana",provider:o}),y}catch(a){return l(a.message||"Failed to connect wallet"),null}},[]),p=react.useCallback(async()=>{l(null);try{let a=(typeof window<"u"?window:null)?.ethereum;if(!a)return l("MetaMask not found"),null;let u=a;a.providers?.length&&(u=a.providers.find(t=>t.isMetaMask)||a);let y=(await u.request({method:"eth_requestAccounts"}))[0];return y?(n({connected:!0,address:y,chain:"ethereum",provider:"metamask"}),y):(l("No account selected"),null)}catch(o){return l(o.message||"Failed to connect wallet"),null}},[]),m=react.useCallback(async(o,a)=>{l(null);let u=a?.chain||e.chain,c=a?.provider||e.provider,y=a?.address||e.address;try{if(u==="solana"){let t=typeof window<"u"?window:null,h=c==="solflare"?t?.solflare:t?.phantom?.solana||t?.solana;if(!h)throw new Error("Wallet not available");let r=new TextEncoder().encode(o),b=await h.signMessage(r,"utf8"),f=new Uint8Array(b.signature||b),g="";for(let I=0;I<f.byteLength;I++)g+=String.fromCharCode(f[I]);return btoa(g)}if(u==="ethereum"){let h=(typeof window<"u"?window:null)?.ethereum;if(h?.providers?.length&&(h=h.providers.find(b=>b.isMetaMask)||h),!h)throw new Error("MetaMask not available");return await h.request({method:"personal_sign",params:[o,y]})}throw new Error("No wallet connected")}catch(t){return l(t.message||"Signing failed"),null}},[e]),s=react.useCallback(()=>{n({connected:false,address:null,chain:null,provider:null}),l(null);},[]);return {wallet:e,error:i,connectSolana:d,connectEVM:p,signMessage:m,disconnect:s}}var Te="https://stacknet.magma-rpc.com/auth/bridge",H="stacknet-auth-bridge";function X(e){let n=e?.bridgeUrl||Te,i=e?.disabled||false,l=react.useRef(null),[d,p]=react.useState({ready:false,known:false,identity:null,identityCount:0,resolvedStackId:null}),m=react.useRef([]),s=react.useRef(false),o=react.useCallback(t=>{let h={...t,protocol:H};s.current&&l.current?.contentWindow?l.current.contentWindow.postMessage(h,new URL(n).origin):m.current.push(h);},[n]);react.useEffect(()=>{if(i)return;let t=r=>{if(!(!r.data||r.data.protocol!==H)){try{if(r.origin!==new URL(n).origin)return}catch{return}switch(r.data.type){case "bridge:ready":s.current=true,p(b=>({...b,ready:true}));for(let b of m.current)l.current?.contentWindow?.postMessage(b,r.origin);m.current=[],l.current?.contentWindow?.postMessage({protocol:H,type:"auth:check"},r.origin),l.current?.contentWindow?.postMessage({protocol:H,type:"auth:resolve-stack"},r.origin);break;case "auth:status":p(b=>({...b,known:r.data.known,identity:r.data.identity,identityCount:r.data.identityCount||0}));break;case "auth:resolved-stack":p(b=>({...b,resolvedStackId:r.data.stackId||null}));break;}}};window.addEventListener("message",t);let h=document.createElement("iframe");return h.src=n,h.style.display="none",h.setAttribute("aria-hidden","true"),h.setAttribute("tabindex","-1"),h.setAttribute("sandbox","allow-scripts allow-same-origin"),document.body.appendChild(h),l.current=h,()=>{window.removeEventListener("message",t),h.parentNode&&h.parentNode.removeChild(h),l.current=null,s.current=false;}},[n,i]);let a=react.useCallback(t=>{o({type:"auth:connected",...t});},[o]),u=react.useCallback(t=>{o({type:"auth:disconnected",...t});},[o]),c=react.useCallback(()=>{o({type:"auth:clear"}),p({ready:d.ready,known:false,identity:null,identityCount:0,resolvedStackId:null});},[o,d.ready]),y=react.useCallback(()=>{o({type:"auth:check"});},[o]);return {...d,reportConnected:a,reportDisconnected:u,clearAll:c,refresh:y}}async function me(e,n,i,l){let d=e.apiVersion||"v2",p=`${e.baseUrl}/api/${d}${i}`;try{let m=await fetch(p,{method:n,headers:{"Content-Type":"application/json"},body:l?JSON.stringify(l):void 0}),s=await m.json();return m.ok?s.success&&s.data!==void 0?{success:!0,data:s.data}:{success:!0,data:s}:{success:!1,error:s.error||{code:"UNKNOWN_ERROR",message:"Unknown error"}}}catch(m){return {success:false,error:{code:"NETWORK_ERROR",message:m instanceof Error?m.message:"Network error"}}}}function he(e){return {getNetworkStatus:()=>me(e,"GET","/network/status"),getWeb3Challenge:(n,i)=>me(e,"POST",`/stacks/${encodeURIComponent(e.stackId)}/auth/web3/challenge`,{chain:n,address:i})}}function Re(e={apiBaseUrl:""}){let{wallet:n,connectSolana:i,connectEVM:l,signMessage:d,disconnect:p}=q(),{session:m,isAuthenticated:s,refresh:o,readSession:a}=W(),{headers:u}=z(),c=X({disabled:typeof window>"u"}),[y,t]=react.useState(false),[h,r]=react.useState(null),[b,f]=react.useState(false),g=e.apiBaseUrl||"",I=e.stacknetUrl||"https://stacknet.magma-rpc.com",C=e.stackId||c.resolvedStackId||"",S=he({baseUrl:I,stackId:C}),_=react.useCallback(async(k,P,T,R)=>{t(true),r(null);try{let v=P;if(!v){let F=await T();if(!F)return t(!1),!1;v=F;}let U=await S.getWeb3Challenge(k,v);if(!U.success||!U.data)return r("Failed to get challenge"),t(!1),!1;let x=await d(U.data.message,{chain:k,provider:R,address:v});if(!x)return t(!1),!1;let B={chain:k,message:U.data.message,signature:x,stackId:C};k==="solana"&&(B.publicKey=v);let J=await fetch(`${g}/api/auth/callback`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify(B)});if(!J.ok){let F=await J.json().catch(()=>({}));return r(F.error||"Authentication failed"),t(!1),!1}return c.reportConnected({address:v,chain:k,method:R||(k==="solana"?"phantom":"metamask"),stackId:C}),a(),t(!1),!0}catch(v){return r(v.message||"Authentication failed"),t(false),false}},[g,S,d,a,c,C]),$=react.useCallback(async(k="phantom")=>{let P=await i(k);return P?_("solana",P,()=>i(k),k):false},[i,_]),L=react.useCallback(async()=>{let k=await l();return k?_("ethereum",k,l,"metamask"):false},[l,_]),w=react.useCallback(async k=>{t(true),r(null);try{let P=await fetch(`${g}/api/auth/otp`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify({code:k})});if(!P.ok){let T=await P.json().catch(()=>({}));return r(T.error||"Invalid code"),t(!1),!1}return a(),t(!1),!0}catch(P){return r(P.message||"OTP verification failed"),t(false),false}},[g,a]),A=react.useCallback(async(k,P)=>{t(true),r(null);try{let T=P||`${window.location.origin}/api/auth/oauth/callback`,R=new URLSearchParams({provider:k,redirectUri:T,stackId:C}),v=await fetch(`${g}/api/auth/oauth/start?${R}`,{credentials:"include"});if(!v.ok){let x=await v.json().catch(()=>({}));return r(x.error||"Failed to start OAuth flow"),t(!1),!1}let U=await v.json();if(U.redirect_url){let x;try{x=new URL(U.redirect_url);}catch{return r("Invalid OAuth redirect URL"),t(!1),!1}let B=[/(^|\.)accounts\.google\.com$/,/(^|\.)discord\.com$/,/(^|\.)github\.com$/,/(^|\.)x\.com$/,/(^|\.)twitter\.com$/,/(^|\.)apple\.com$/];return x.protocol!=="https:"||!B.some(J=>J.test(x.hostname))?(r(`Refusing to redirect to non-OAuth host: ${x.hostname}`),t(!1),!1):(typeof sessionStorage<"u"&&(sessionStorage.setItem("oauth_state",U.state||""),sessionStorage.setItem("oauth_provider",k)),window.location.href=x.toString(),!0)}return r("No redirect URL returned"),t(!1),!1}catch(T){return r(T.message||"OAuth flow failed"),t(false),false}},[g,C]),M=react.useCallback(async(k,P,T)=>{t(true),r(null);try{if(typeof sessionStorage<"u"){let v=sessionStorage.getItem("oauth_state"),U=sessionStorage.getItem("oauth_provider");if(sessionStorage.removeItem("oauth_state"),sessionStorage.removeItem("oauth_provider"),!v||v!==T)return r("OAuth state mismatch \u2014 refusing to complete login"),t(!1),!1;if(U&&U!==k)return r("OAuth provider mismatch \u2014 refusing to complete login"),t(!1),!1}let R=await fetch(`${g}/api/auth/oauth/callback`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify({provider:k,code:P,state:T,stackId:C})});if(!R.ok){let v=await R.json().catch(()=>({}));return r(v.error||"OAuth authentication failed"),t(!1),!1}return a(),t(!1),!0}catch(R){return r(R.message||"OAuth callback failed"),t(false),false}},[g,C,a]),G=react.useCallback(async()=>{n.address&&n.chain&&c.reportDisconnected({address:n.address,chain:n.chain,stackId:C});try{await fetch(`${g}/api/auth/logout`,{method:"POST",headers:u,credentials:"include"});}catch{}p(),a();},[g,u,p,a,n,c,C]);return react.useEffect(()=>{if(!e.autoConnect||b||s||!c.ready||!c.known||!c.identity)return;f(true);let{chain:k,method:P}=c.identity;k==="solana"&&(P==="phantom"||P==="solflare")?$(P):k==="ethereum"&&L();},[e.autoConnect,b,s,c,$,L]),{session:m,isAuthenticated:s,wallet:n,loading:y,error:h,authenticateSolana:$,authenticateEVM:L,authenticateOTP:w,authenticateOAuth:A,authenticateOAuthCallback:M,logout:G,refresh:()=>o(g),stackId:C,bridge:{ready:c.ready,known:c.known,identity:c.identity,identityCount:c.identityCount,resolvedStackId:c.resolvedStackId}}}function ee(e,n="https://stacknet.magma-rpc.com"){let[i,l]=react.useState(null),[d,p]=react.useState(false),[m,s]=react.useState(null),o=react.useCallback(async u=>{p(true),s(null);try{let c=await fetch(`${n}/api/v2/stacks/${u}`);if(!c.ok)return s("Stack not found"),p(!1),null;let y=await c.json(),t=y.data?.stack||y.stack||y,h={id:t.id,name:t.name,displayName:t.displayName||t.name,description:t.description,logoUrl:t.logoUrl,webPageUrl:t.webPageUrl,allowedChains:t.allowedChains||[],features:t.features,stripeProvider:t.stripeProvider,oauthProviders:t.oauthProviders?.map(r=>({provider:r.provider,clientId:r.clientId,enabled:r.enabled!==!1}))};return l(h),p(!1),h}catch(c){return s(c.message),p(false),null}},[n]);react.useEffect(()=>{e&&o(e);},[e,o]);let a=i?Ee(i):[];return {config:i,loading:d,error:m,identityProviders:a,fetchConfig:o}}function Ee(e){let n=[];if(e.features?.web3Auth!==false&&(e.allowedChains.includes("solana")&&(n.push({type:"wallet",id:"phantom",name:"Phantom",chain:"solana"}),n.push({type:"wallet",id:"solflare",name:"Solflare",chain:"solana"})),(e.allowedChains.includes("ethereum")||e.allowedChains.includes("polygon")||e.allowedChains.includes("base"))&&n.push({type:"wallet",id:"metamask",name:"MetaMask",chain:"ethereum"})),e.features?.apiKeyAuth!==false&&n.push({type:"otp",id:"otp",name:"Access Code"}),e.features?.oauthAuth&&e.oauthProviders)for(let i of e.oauthProviders)i.enabled&&n.push({type:"oauth",id:i.provider,name:i.provider});return n}function Me(e=""){let[n,i]=react.useState([]),[l,d]=react.useState(true),[p,m]=react.useState(null),s=react.useCallback(async()=>{try{let o=await fetch(`${e}/api/billing/plans`);if(o.ok){let a=await o.json();i(a.plans||a||[]);}}catch(o){m(o.message);}finally{d(false);}},[e]);return react.useEffect(()=>{s();},[s]),{plans:n,loading:l,error:p,refresh:s}}function Ne(e=""){let[n,i]=react.useState(null),[l,d]=react.useState(true),[p,m]=react.useState(null),s=react.useCallback(async()=>{try{let u=await fetch(`${e}/api/billing/subscription`);if(u.ok){let c=await u.json();i(c.plan?c:null);}}catch(u){m(u.message);}finally{d(false);}},[e]);react.useEffect(()=>{s();},[s]);let o=react.useCallback(async u=>{let c=O(),t=await(await fetch(`${e}/api/billing/subscribe`,{method:"POST",headers:{"Content-Type":"application/json",...c?{"x-csrf-token":c}:{}},body:JSON.stringify({planId:u})})).json();return t.url||t.checkoutUrl||null},[e]),a=react.useCallback(async()=>{let u=O();return (await fetch(`${e}/api/billing/cancel`,{method:"POST",headers:u?{"x-csrf-token":u}:{}})).ok?(await s(),true):false},[e,s]);return {subscription:n,loading:l,error:p,refresh:s,subscribe:o,cancel:a}}function Be(e=""){let[n,i]=react.useState(null),[l,d]=react.useState(true),[p,m]=react.useState(null),s=react.useCallback(async()=>{try{let o=await fetch(`${e}/api/billing/usage`);if(o.ok){let a=await o.json();i(a);}}catch(o){m(o.message);}finally{d(false);}},[e]);return react.useEffect(()=>{s();},[s]),{usage:n,loading:l,error:p,refresh:s}}function Je(e=""){let[n,i]=react.useState(false),[l,d]=react.useState(null),p=react.useCallback(async s=>{i(true),d(null);try{let o=O(),a=await fetch(`${e}/api/billing/prepaid`,{method:"POST",headers:{"Content-Type":"application/json",...o?{"x-csrf-token":o}:{}},body:JSON.stringify({amountCents:s})}),u=await a.json();return a.ok?u.url||null:(d(u.error||"Purchase failed"),null)}catch(o){return d(o.message),null}finally{i(false);}},[e]),m=react.useCallback(async s=>{i(true),d(null);try{let o=O(),a=await fetch(`${e}/api/billing/verify-prepaid`,{method:"POST",headers:{"Content-Type":"application/json",...o?{"x-csrf-token":o}:{}},body:JSON.stringify({sessionId:s})}),u=await a.json();return a.ok?u:(d(u.error||"Verification failed"),null)}catch(o){return d(o.message),null}finally{i(false);}},[e]);return {purchase:p,verifySession:m,loading:n,error:l}}function He(e="",n){let[i,l]=react.useState([]),[d,p]=react.useState(true),[m,s]=react.useState(null),o=n?.limit||50,a=n?.offset||0,u=react.useCallback(async()=>{try{let c=await fetch(`${e}/api/billing/history?limit=${o}&offset=${a}`);if(c.ok){let y=await c.json();l(y.records||y.history||(Array.isArray(y)?y:[]));}}catch(c){s(c.message);}finally{p(false);}},[e,o,a]);return react.useEffect(()=>{u();},[u]),{records:i,loading:d,error:m,refresh:u}}function Ve(){if(typeof document>"u")return null;let e=document.cookie.split(";").map(n=>n.trim()).find(n=>n.startsWith("__csrf="));return e?e.slice(7):null}function ze(e,n){let[i,l]=react.useState(null),[d,p]=react.useState(true),[m,s]=react.useState(false),[o,a]=react.useState(null),u=n?.apiBaseUrl??"",c=n?.scope===void 0||n?.scope==="global"?"global":`stack:${n.scope.stackId}`,y=react.useCallback(r=>{let b=encodeURIComponent(r);if(c==="global")return `${u}/api/user/profile/${b}`;let f=c.slice(6);return `${u}/api/v2/stacks/${encodeURIComponent(f)}/members/${b}/profile`},[u,c]),t=react.useCallback(async()=>{if(!e){l(null),p(false);return}p(true),a(null);try{let r=await fetch(y(e));if(r.ok){let b=await r.json(),f=b.profile||b.data?.profile||b;l({mid:f.mid||e,username:f.username||"",avatarUrl:f.avatar_url||f.avatarUrl,bio:f.bio,paymentAddress:f.payment_address||f.paymentAddress,createdAt:f.created_at||f.createdAt,updatedAt:f.updated_at||f.updatedAt});}else if(r.status===404)l({mid:e,username:""});else throw new Error(`${r.status}`)}catch(r){a(r instanceof Error?r.message:"Failed to load profile");}finally{p(false);}},[e,y]);react.useEffect(()=>{t();},[t]);let h=react.useCallback(async r=>{if(!e)return false;s(true),a(null);try{let b=Ve(),f={};r.username!==void 0&&(f.username=r.username),r.avatarUrl!==void 0&&(f.avatar_url=r.avatarUrl),r.bio!==void 0&&(f.bio=r.bio),r.paymentAddress!==void 0&&(f.payment_address=r.paymentAddress);let g=await fetch(y(e),{method:"PUT",headers:{"Content-Type":"application/json",...b?{"x-csrf-token":b}:{}},credentials:"same-origin",body:JSON.stringify(f)});if(!g.ok){let S=await g.json().catch(()=>({}));throw new Error(S.error||S.message||`Update failed: ${g.status}`)}let I=await g.json(),C=I.profile||I.data?.profile||I;return l(S=>({mid:S?.mid||e,username:r.username??S?.username??"",avatarUrl:r.avatarUrl??S?.avatarUrl,bio:r.bio??S?.bio,paymentAddress:r.paymentAddress??C.payment_address??C.paymentAddress??S?.paymentAddress,createdAt:S?.createdAt,updatedAt:C.updated_at||C.updatedAt||Date.now()})),!0}catch(b){return a(b instanceof Error?b.message:"Update failed"),false}finally{s(false);}},[e,y]);return {profile:i,loading:d,saving:m,error:o,updateProfile:h,refresh:t}}var ke="google-identity-services",qe="https://accounts.google.com/gsi/client";function Qe({stackId:e,stacknetUrl:n="https://stacknet.magma-rpc.com",apiBaseUrl:i="",autoPrompt:l=true,cancelOnTapOutside:d=true,onSuccess:p,onError:m,disabled:s=false}){let{config:o}=ee(e,n),{isAuthenticated:a,loading:u,readSession:c}=W(),[y,t]=react.useState(false),[h,r]=react.useState(null),[b,f]=react.useState(false),g=react.useRef(false),I=react.useRef(false),S=o?.oauthProviders?.find(w=>w.provider==="google"&&w.enabled&&w.clientId)?.clientId||null;react.useEffect(()=>{if(s||!S||typeof window>"u")return;if(document.getElementById(ke)){f(true);return}let w=document.createElement("script");w.id=ke,w.src=qe,w.async=true,w.defer=true,w.onload=()=>f(true),w.onerror=()=>{r("Failed to load Google sign-in"),m?.("Failed to load Google Identity Services script");},document.head.appendChild(w);},[s,S,m]);let _=react.useCallback(async w=>{t(true),r(null);try{let A=await fetch(`${i}/api/auth/google/one-tap`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({credential:w.credential,stackId:e})});if(!A.ok){let G=(await A.json().catch(()=>({}))).error||"Google sign-in failed";r(G),m?.(G),t(!1);return}c(),t(!1),p?.();}catch(A){let M=A.message||"Google sign-in failed";r(M),m?.(M),t(false);}},[i,e,c,p,m]);react.useEffect(()=>{if(s){console.debug("[GoogleOneTap] Disabled");return}if(!b){console.debug("[GoogleOneTap] Script not loaded yet, clientId:",S);return}if(!S){console.debug("[GoogleOneTap] No Google clientId from stack config");return}if(u){console.debug("[GoogleOneTap] Session still loading");return}if(a){console.debug("[GoogleOneTap] User already authenticated, skipping");return}if(!window.google?.accounts?.id){console.debug("[GoogleOneTap] GIS library not available on window");return}g.current||(console.debug("[GoogleOneTap] Initializing with clientId:",S),g.current=true,window.google.accounts.id.initialize({client_id:S,callback:_,auto_select:true,cancel_on_tap_outside:d}),l&&!I.current&&(I.current=true,console.debug("[GoogleOneTap] Showing prompt..."),window.google.accounts.id.prompt(w=>{w.isDisplayed?.()&&console.debug("[GoogleOneTap] Prompt displayed"),w.isNotDisplayed?.()&&console.debug("[GoogleOneTap] Not displayed:",w.getNotDisplayedReason?.()),w.isSkippedMoment?.()&&console.debug("[GoogleOneTap] Skipped:",w.getSkippedReason?.()),w.isDismissedMoment?.()&&console.debug("[GoogleOneTap] Dismissed:",w.getDismissedReason?.());})));},[s,b,S,u,a,_,l,d]),react.useEffect(()=>()=>{window.google?.accounts?.id&&g.current&&window.google.accounts.id.cancel();},[]);let $=react.useCallback(()=>{!window.google?.accounts?.id||!g.current||window.google.accounts.id.prompt();},[]),L=react.useCallback((w,A)=>{!w||!window.google?.accounts?.id||!g.current||window.google.accounts.id.renderButton(w,{theme:A?.theme||"filled_black",size:A?.size||"large",text:A?.text||"signin_with",width:A?.width});},[]);return {available:!!S,ready:b&&!!S,loading:y,error:h,prompt:$,renderButton:L,clientId:S}}var Ye="/api";function Ze(e,n){return `${(n||(typeof window<"u"?window.location.origin:"")).replace(/\/$/,"")}/?ref=${encodeURIComponent(e)}`}function et(e={}){let{shareBaseUrl:n,autoMint:i=true}=e,[l,d]=react.useState(null),[p,m]=react.useState(true),[s,o]=react.useState(null),a=react.useCallback(async()=>{m(true),o(null);try{let y=i?"POST":"GET",t=await fetch(`${Ye}/social/join-code`,{method:y,credentials:"include"});if(!t.ok){d(null),t.status===401?o("not_authenticated"):o(`HTTP ${t.status}`);return}let h=await t.json();d(h?.code??null);}catch(y){o(y?.message||"network_error"),d(null);}finally{m(false);}},[i]);react.useEffect(()=>{a();},[a]);let u=l?Ze(l,n):null,c=react.useCallback(async()=>{if(!u)return false;try{if(typeof navigator<"u"&&navigator.clipboard?.writeText)return await navigator.clipboard.writeText(u),!0}catch{}return false},[u]);return {code:l,shareUrl:u,loading:p,error:s,refresh:a,copyShareLink:c}}var Ce="/api";function rt(e={}){let{shareBaseUrl:n}=e,[i,l]=react.useState([]),[d,p]=react.useState(null),[m,s]=react.useState(true),[o,a]=react.useState(false),[u,c]=react.useState(null),y=react.useCallback(async()=>{s(true),c(null);try{let f=await fetch(`${Ce}/social/invites`,{method:"GET",credentials:"include"});if(!f.ok){f.status===401?c("not_authenticated"):c(`HTTP ${f.status}`),l([]),p(null);return}let g=await f.json();l(Array.isArray(g.codes)?g.codes:[]),p(typeof g.limit=="number"?g.limit:null);}catch(f){c(f?.message||"network_error"),l([]),p(null);}finally{s(false);}},[]);react.useEffect(()=>{y();},[y]);let t=react.useCallback(async()=>{a(true),c(null);try{let f=await fetch(`${Ce}/social/invites`,{method:"POST",credentials:"include"}),g=await f.json().catch(()=>({}));return f.ok?(Array.isArray(g.codes)&&l(g.codes),typeof g.limit=="number"&&p(g.limit),g.minted??null):(f.status===409&&g.error==="code_limit_reached"?c("code_limit_reached"):f.status===401?c("not_authenticated"):c(g.error||`HTTP ${f.status}`),null)}catch(f){return c(f?.message||"network_error"),null}finally{a(false);}},[]),h=react.useCallback(f=>{let g=n??(typeof window<"u"?window.location.origin:null);return g?`${g.replace(/\/$/,"")}/?ref=${encodeURIComponent(f)}`:null},[n]),r=react.useCallback(async f=>{let g=h(f);if(!g)return false;try{if(typeof navigator<"u"&&navigator.clipboard?.writeText)return await navigator.clipboard.writeText(g),!0}catch{}return false},[h]),b=react.useMemo(()=>d==null?null:Math.max(0,d-i.length),[d,i.length]);return {codes:i,limit:d,remaining:b,loading:m,minting:o,error:u,refresh:y,mint:t,buildShareUrl:h,copyShareLink:r}}
2
+ exports.useAuthBridge=X;exports.useBillingHistory=He;exports.useCSRFToken=z;exports.useGoogleOneTap=Qe;exports.useInvites=rt;exports.useJoinCode=et;exports.usePlans=Me;exports.usePrepaidCheckout=Je;exports.useProfile=ze;exports.useSession=W;exports.useStackAuth=Re;exports.useStackConfig=ee;exports.useSubscription=Ne;exports.useUsage=Be;exports.useWeb3Wallet=q;
@@ -344,4 +344,58 @@ interface UseJoinCodeConfig {
344
344
  }
345
345
  declare function useJoinCode(config?: UseJoinCodeConfig): UseJoinCodeResult;
346
346
 
347
- export { type BridgeIdentity, type GoogleOneTapConfig, type StackPublicConfig, type UseJoinCodeConfig, type UseJoinCodeResult, type WalletState, useAuthBridge, useBillingHistory, useCSRFToken, useGoogleOneTap, useJoinCode, usePlans, usePrepaidCheckout, useProfile, useSession, useStackAuth, useStackConfig, useSubscription, useUsage, useWeb3Wallet };
347
+ /**
348
+ * useInvites — fetch the signed-in user's full set of invite codes
349
+ * and let them mint new single-use codes (up to the per-owner cap).
350
+ *
351
+ * Backed by `/api/social/invites` (GET to list, POST to mint). Each
352
+ * code is single-use across the whole network: once redeemed via
353
+ * `?ref=<code>` on signup, the corresponding entry's `consumedByMid`
354
+ * + `consumedAt` are populated and the code can no longer be used.
355
+ *
356
+ * Use this in the new multi-slot invite UI. The legacy `useJoinCode`
357
+ * single-code hook still works for back-compat (it returns the user's
358
+ * first available code, minting one if none).
359
+ */
360
+ interface InviteCode {
361
+ code: string;
362
+ ownerMid: string;
363
+ createdAt: number;
364
+ timesUsed: number;
365
+ lastUsedAt: number | null;
366
+ consumedByMid: string | null;
367
+ consumedAt: number | null;
368
+ }
369
+ interface UseInvitesResult {
370
+ /** All of the user's codes (consumed + available), oldest first. */
371
+ codes: InviteCode[];
372
+ /** Per-owner cap from the server (e.g. 10). Null until first load. */
373
+ limit: number | null;
374
+ /** Slots still available to mint (limit - codes.length). */
375
+ remaining: number | null;
376
+ /** True while the initial list fetch is in flight. */
377
+ loading: boolean;
378
+ /** True while a mint POST is in flight. */
379
+ minting: boolean;
380
+ /** Last error (load or mint), if any. */
381
+ error: string | null;
382
+ /** Re-fetch the code list from the server. */
383
+ refresh: () => Promise<void>;
384
+ /** Mint a fresh single-use code. Throws on cap-reached / network error. */
385
+ mint: () => Promise<InviteCode | null>;
386
+ /**
387
+ * Build the share URL for a given code. Defaults to
388
+ * `${origin}/?ref=<code>`. Returns null in non-browser contexts when
389
+ * `shareBaseUrl` isn't supplied.
390
+ */
391
+ buildShareUrl: (code: string) => string | null;
392
+ /** Copy the share URL for a given code to the clipboard. */
393
+ copyShareLink: (code: string) => Promise<boolean>;
394
+ }
395
+ interface UseInvitesConfig {
396
+ /** Override the share-URL origin. Defaults to `window.location.origin`. */
397
+ shareBaseUrl?: string;
398
+ }
399
+ declare function useInvites(config?: UseInvitesConfig): UseInvitesResult;
400
+
401
+ export { type BridgeIdentity, type GoogleOneTapConfig, type InviteCode, type StackPublicConfig, type UseInvitesConfig, type UseInvitesResult, type UseJoinCodeConfig, type UseJoinCodeResult, type WalletState, useAuthBridge, useBillingHistory, useCSRFToken, useGoogleOneTap, useInvites, useJoinCode, usePlans, usePrepaidCheckout, useProfile, useSession, useStackAuth, useStackConfig, useSubscription, useUsage, useWeb3Wallet };
@@ -344,4 +344,58 @@ interface UseJoinCodeConfig {
344
344
  }
345
345
  declare function useJoinCode(config?: UseJoinCodeConfig): UseJoinCodeResult;
346
346
 
347
- export { type BridgeIdentity, type GoogleOneTapConfig, type StackPublicConfig, type UseJoinCodeConfig, type UseJoinCodeResult, type WalletState, useAuthBridge, useBillingHistory, useCSRFToken, useGoogleOneTap, useJoinCode, usePlans, usePrepaidCheckout, useProfile, useSession, useStackAuth, useStackConfig, useSubscription, useUsage, useWeb3Wallet };
347
+ /**
348
+ * useInvites — fetch the signed-in user's full set of invite codes
349
+ * and let them mint new single-use codes (up to the per-owner cap).
350
+ *
351
+ * Backed by `/api/social/invites` (GET to list, POST to mint). Each
352
+ * code is single-use across the whole network: once redeemed via
353
+ * `?ref=<code>` on signup, the corresponding entry's `consumedByMid`
354
+ * + `consumedAt` are populated and the code can no longer be used.
355
+ *
356
+ * Use this in the new multi-slot invite UI. The legacy `useJoinCode`
357
+ * single-code hook still works for back-compat (it returns the user's
358
+ * first available code, minting one if none).
359
+ */
360
+ interface InviteCode {
361
+ code: string;
362
+ ownerMid: string;
363
+ createdAt: number;
364
+ timesUsed: number;
365
+ lastUsedAt: number | null;
366
+ consumedByMid: string | null;
367
+ consumedAt: number | null;
368
+ }
369
+ interface UseInvitesResult {
370
+ /** All of the user's codes (consumed + available), oldest first. */
371
+ codes: InviteCode[];
372
+ /** Per-owner cap from the server (e.g. 10). Null until first load. */
373
+ limit: number | null;
374
+ /** Slots still available to mint (limit - codes.length). */
375
+ remaining: number | null;
376
+ /** True while the initial list fetch is in flight. */
377
+ loading: boolean;
378
+ /** True while a mint POST is in flight. */
379
+ minting: boolean;
380
+ /** Last error (load or mint), if any. */
381
+ error: string | null;
382
+ /** Re-fetch the code list from the server. */
383
+ refresh: () => Promise<void>;
384
+ /** Mint a fresh single-use code. Throws on cap-reached / network error. */
385
+ mint: () => Promise<InviteCode | null>;
386
+ /**
387
+ * Build the share URL for a given code. Defaults to
388
+ * `${origin}/?ref=<code>`. Returns null in non-browser contexts when
389
+ * `shareBaseUrl` isn't supplied.
390
+ */
391
+ buildShareUrl: (code: string) => string | null;
392
+ /** Copy the share URL for a given code to the clipboard. */
393
+ copyShareLink: (code: string) => Promise<boolean>;
394
+ }
395
+ interface UseInvitesConfig {
396
+ /** Override the share-URL origin. Defaults to `window.location.origin`. */
397
+ shareBaseUrl?: string;
398
+ }
399
+ declare function useInvites(config?: UseInvitesConfig): UseInvitesResult;
400
+
401
+ export { type BridgeIdentity, type GoogleOneTapConfig, type InviteCode, type StackPublicConfig, type UseInvitesConfig, type UseInvitesResult, type UseJoinCodeConfig, type UseJoinCodeResult, type WalletState, useAuthBridge, useBillingHistory, useCSRFToken, useGoogleOneTap, useInvites, useJoinCode, usePlans, usePrepaidCheckout, useProfile, useSession, useStackAuth, useStackConfig, useSubscription, useUsage, useWeb3Wallet };
@@ -1,2 +1,2 @@
1
- import {useState,useCallback,useEffect,useRef}from'react';function ce(){if(typeof document>"u")return null;try{let e=document.cookie.split(";").map(i=>i.trim()).find(i=>i.startsWith("stackauth_session="));if(!e)return null;let n=e.slice(18);return JSON.parse(atob(n.replace(/-/g,"+").replace(/_/g,"/")))}catch{return null}}function x(e="__csrf"){if(typeof document>"u")return null;let n=document.cookie.split(";").map(i=>i.trim()).find(i=>i.startsWith(`${e}=`));return n?n.slice(e.length+1):null}function L(){let[e,n]=useState(null),[i,l]=useState(true),d=useCallback(()=>{let o=ce();o&&o.expiresAt>Date.now()?n({userId:o.userId,address:o.address,chain:o.chain,expiresAt:o.expiresAt,planId:o.planId,authMethod:o.authMethod}):n(null),l(false);},[]);useEffect(()=>{d();},[d]);let g=useCallback(async(o="")=>{try{let s=await fetch(`${o}/api/auth/session`);if(s.ok){let a=await s.json();if(a.session)return n(a.session),a.session}return n(null),null}catch{return null}},[]),f=!!e&&e.expiresAt>Date.now();return {session:e,loading:i,isAuthenticated:f,refresh:g,readSession:d}}function K(e="__csrf",n="x-csrf-token"){let[i,l]=useState(null);useEffect(()=>{l(x(e));},[e]);let d=i?{[n]:i}:{};return {token:i,headers:d}}function V(){let[e,n]=useState({connected:false,address:null,chain:null,provider:null}),[i,l]=useState(null),d=useCallback(async(s="phantom")=>{l(null);try{let a=typeof window<"u"?window:null,c=s==="phantom"?a?.phantom?.solana||a?.solana:a?.solflare;if(!c)return l(`${s} wallet not found`),null;let m=(await c.connect()).publicKey.toString();return n({connected:!0,address:m,chain:"solana",provider:s}),m}catch(a){return l(a.message||"Failed to connect wallet"),null}},[]),g=useCallback(async()=>{l(null);try{let a=(typeof window<"u"?window:null)?.ethereum;if(!a)return l("MetaMask not found"),null;let c=a;a.providers?.length&&(c=a.providers.find(t=>t.isMetaMask)||a);let m=(await c.request({method:"eth_requestAccounts"}))[0];return m?(n({connected:!0,address:m,chain:"ethereum",provider:"metamask"}),m):(l("No account selected"),null)}catch(s){return l(s.message||"Failed to connect wallet"),null}},[]),f=useCallback(async(s,a)=>{l(null);let c=a?.chain||e.chain,u=a?.provider||e.provider,m=a?.address||e.address;try{if(c==="solana"){let t=typeof window<"u"?window:null,p=u==="solflare"?t?.solflare:t?.phantom?.solana||t?.solana;if(!p)throw new Error("Wallet not available");let r=new TextEncoder().encode(s),h=await p.signMessage(r,"utf8"),y=new Uint8Array(h.signature||h),w="";for(let A=0;A<y.byteLength;A++)w+=String.fromCharCode(y[A]);return btoa(w)}if(c==="ethereum"){let p=(typeof window<"u"?window:null)?.ethereum;if(p?.providers?.length&&(p=p.providers.find(h=>h.isMetaMask)||p),!p)throw new Error("MetaMask not available");return await p.request({method:"personal_sign",params:[s,m]})}throw new Error("No wallet connected")}catch(t){return l(t.message||"Signing failed"),null}},[e]),o=useCallback(()=>{n({connected:false,address:null,chain:null,provider:null}),l(null);},[]);return {wallet:e,error:i,connectSolana:d,connectEVM:g,signMessage:f,disconnect:o}}var Ae="https://stacknet.magma-rpc.com/auth/bridge",D="stacknet-auth-bridge";function q(e){let n=e?.bridgeUrl||Ae,i=e?.disabled||false,l=useRef(null),[d,g]=useState({ready:false,known:false,identity:null,identityCount:0,resolvedStackId:null}),f=useRef([]),o=useRef(false),s=useCallback(t=>{let p={...t,protocol:D};o.current&&l.current?.contentWindow?l.current.contentWindow.postMessage(p,new URL(n).origin):f.current.push(p);},[n]);useEffect(()=>{if(i)return;let t=r=>{if(!(!r.data||r.data.protocol!==D)){try{if(r.origin!==new URL(n).origin)return}catch{return}switch(r.data.type){case "bridge:ready":o.current=true,g(h=>({...h,ready:true}));for(let h of f.current)l.current?.contentWindow?.postMessage(h,r.origin);f.current=[],l.current?.contentWindow?.postMessage({protocol:D,type:"auth:check"},r.origin),l.current?.contentWindow?.postMessage({protocol:D,type:"auth:resolve-stack"},r.origin);break;case "auth:status":g(h=>({...h,known:r.data.known,identity:r.data.identity,identityCount:r.data.identityCount||0}));break;case "auth:resolved-stack":g(h=>({...h,resolvedStackId:r.data.stackId||null}));break;}}};window.addEventListener("message",t);let p=document.createElement("iframe");return p.src=n,p.style.display="none",p.setAttribute("aria-hidden","true"),p.setAttribute("tabindex","-1"),p.setAttribute("sandbox","allow-scripts allow-same-origin"),document.body.appendChild(p),l.current=p,()=>{window.removeEventListener("message",t),p.parentNode&&p.parentNode.removeChild(p),l.current=null,o.current=false;}},[n,i]);let a=useCallback(t=>{s({type:"auth:connected",...t});},[s]),c=useCallback(t=>{s({type:"auth:disconnected",...t});},[s]),u=useCallback(()=>{s({type:"auth:clear"}),g({ready:d.ready,known:false,identity:null,identityCount:0,resolvedStackId:null});},[s,d.ready]),m=useCallback(()=>{s({type:"auth:check"});},[s]);return {...d,reportConnected:a,reportDisconnected:c,clearAll:u,refresh:m}}async function ge(e,n,i,l){let d=e.apiVersion||"v2",g=`${e.baseUrl}/api/${d}${i}`;try{let f=await fetch(g,{method:n,headers:{"Content-Type":"application/json"},body:l?JSON.stringify(l):void 0}),o=await f.json();return f.ok?o.success&&o.data!==void 0?{success:!0,data:o.data}:{success:!0,data:o}:{success:!1,error:o.error||{code:"UNKNOWN_ERROR",message:"Unknown error"}}}catch(f){return {success:false,error:{code:"NETWORK_ERROR",message:f instanceof Error?f.message:"Network error"}}}}function pe(e){return {getNetworkStatus:()=>ge(e,"GET","/network/status"),getWeb3Challenge:(n,i)=>ge(e,"POST",`/stacks/${encodeURIComponent(e.stackId)}/auth/web3/challenge`,{chain:n,address:i})}}function Te(e={apiBaseUrl:""}){let{wallet:n,connectSolana:i,connectEVM:l,signMessage:d,disconnect:g}=V(),{session:f,isAuthenticated:o,refresh:s,readSession:a}=L(),{headers:c}=K(),u=q({disabled:typeof window>"u"}),[m,t]=useState(false),[p,r]=useState(null),[h,y]=useState(false),w=e.apiBaseUrl||"",A=e.stacknetUrl||"https://stacknet.magma-rpc.com",C=e.stackId||u.resolvedStackId||"",S=pe({baseUrl:A,stackId:C}),E=useCallback(async(k,P,O,R)=>{t(true),r(null);try{let v=P;if(!v){let B=await O();if(!B)return t(!1),!1;v=B;}let T=await S.getWeb3Challenge(k,v);if(!T.success||!T.data)return r("Failed to get challenge"),t(!1),!1;let U=await d(T.data.message,{chain:k,provider:R,address:v});if(!U)return t(!1),!1;let G={chain:k,message:T.data.message,signature:U,stackId:C};k==="solana"&&(G.publicKey=v);let J=await fetch(`${w}/api/auth/callback`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify(G)});if(!J.ok){let B=await J.json().catch(()=>({}));return r(B.error||"Authentication failed"),t(!1),!1}return u.reportConnected({address:v,chain:k,method:R||(k==="solana"?"phantom":"metamask"),stackId:C}),a(),t(!1),!0}catch(v){return r(v.message||"Authentication failed"),t(false),false}},[w,S,d,a,u,C]),$=useCallback(async(k="phantom")=>{let P=await i(k);return P?E("solana",P,()=>i(k),k):false},[i,E]),W=useCallback(async()=>{let k=await l();return k?E("ethereum",k,l,"metamask"):false},[l,E]),b=useCallback(async k=>{t(true),r(null);try{let P=await fetch(`${w}/api/auth/otp`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify({code:k})});if(!P.ok){let O=await P.json().catch(()=>({}));return r(O.error||"Invalid code"),t(!1),!1}return a(),t(!1),!0}catch(P){return r(P.message||"OTP verification failed"),t(false),false}},[w,a]),I=useCallback(async(k,P)=>{t(true),r(null);try{let O=P||`${window.location.origin}/api/auth/oauth/callback`,R=new URLSearchParams({provider:k,redirectUri:O,stackId:C}),v=await fetch(`${w}/api/auth/oauth/start?${R}`,{credentials:"include"});if(!v.ok){let U=await v.json().catch(()=>({}));return r(U.error||"Failed to start OAuth flow"),t(!1),!1}let T=await v.json();if(T.redirect_url){let U;try{U=new URL(T.redirect_url);}catch{return r("Invalid OAuth redirect URL"),t(!1),!1}let G=[/(^|\.)accounts\.google\.com$/,/(^|\.)discord\.com$/,/(^|\.)github\.com$/,/(^|\.)x\.com$/,/(^|\.)twitter\.com$/,/(^|\.)apple\.com$/];return U.protocol!=="https:"||!G.some(J=>J.test(U.hostname))?(r(`Refusing to redirect to non-OAuth host: ${U.hostname}`),t(!1),!1):(typeof sessionStorage<"u"&&(sessionStorage.setItem("oauth_state",T.state||""),sessionStorage.setItem("oauth_provider",k)),window.location.href=U.toString(),!0)}return r("No redirect URL returned"),t(!1),!1}catch(O){return r(O.message||"OAuth flow failed"),t(false),false}},[w,C]),N=useCallback(async(k,P,O)=>{t(true),r(null);try{if(typeof sessionStorage<"u"){let v=sessionStorage.getItem("oauth_state"),T=sessionStorage.getItem("oauth_provider");if(sessionStorage.removeItem("oauth_state"),sessionStorage.removeItem("oauth_provider"),!v||v!==O)return r("OAuth state mismatch \u2014 refusing to complete login"),t(!1),!1;if(T&&T!==k)return r("OAuth provider mismatch \u2014 refusing to complete login"),t(!1),!1}let R=await fetch(`${w}/api/auth/oauth/callback`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify({provider:k,code:P,state:O,stackId:C})});if(!R.ok){let v=await R.json().catch(()=>({}));return r(v.error||"OAuth authentication failed"),t(!1),!1}return a(),t(!1),!0}catch(R){return r(R.message||"OAuth callback failed"),t(false),false}},[w,C,a]),j=useCallback(async()=>{n.address&&n.chain&&u.reportDisconnected({address:n.address,chain:n.chain,stackId:C});try{await fetch(`${w}/api/auth/logout`,{method:"POST",headers:c,credentials:"include"});}catch{}g(),a();},[w,c,g,a,n,u,C]);return useEffect(()=>{if(!e.autoConnect||h||o||!u.ready||!u.known||!u.identity)return;y(true);let{chain:k,method:P}=u.identity;k==="solana"&&(P==="phantom"||P==="solflare")?$(P):k==="ethereum"&&W();},[e.autoConnect,h,o,u,$,W]),{session:f,isAuthenticated:o,wallet:n,loading:m,error:p,authenticateSolana:$,authenticateEVM:W,authenticateOTP:b,authenticateOAuth:I,authenticateOAuthCallback:N,logout:j,refresh:()=>s(w),stackId:C,bridge:{ready:u.ready,known:u.known,identity:u.identity,identityCount:u.identityCount,resolvedStackId:u.resolvedStackId}}}function Y(e,n="https://stacknet.magma-rpc.com"){let[i,l]=useState(null),[d,g]=useState(false),[f,o]=useState(null),s=useCallback(async c=>{g(true),o(null);try{let u=await fetch(`${n}/api/v2/stacks/${c}`);if(!u.ok)return o("Stack not found"),g(!1),null;let m=await u.json(),t=m.data?.stack||m.stack||m,p={id:t.id,name:t.name,displayName:t.displayName||t.name,description:t.description,logoUrl:t.logoUrl,webPageUrl:t.webPageUrl,allowedChains:t.allowedChains||[],features:t.features,stripeProvider:t.stripeProvider,oauthProviders:t.oauthProviders?.map(r=>({provider:r.provider,clientId:r.clientId,enabled:r.enabled!==!1}))};return l(p),g(!1),p}catch(u){return o(u.message),g(false),null}},[n]);useEffect(()=>{e&&s(e);},[e,s]);let a=i?Re(i):[];return {config:i,loading:d,error:f,identityProviders:a,fetchConfig:s}}function Re(e){let n=[];if(e.features?.web3Auth!==false&&(e.allowedChains.includes("solana")&&(n.push({type:"wallet",id:"phantom",name:"Phantom",chain:"solana"}),n.push({type:"wallet",id:"solflare",name:"Solflare",chain:"solana"})),(e.allowedChains.includes("ethereum")||e.allowedChains.includes("polygon")||e.allowedChains.includes("base"))&&n.push({type:"wallet",id:"metamask",name:"MetaMask",chain:"ethereum"})),e.features?.apiKeyAuth!==false&&n.push({type:"otp",id:"otp",name:"Access Code"}),e.features?.oauthAuth&&e.oauthProviders)for(let i of e.oauthProviders)i.enabled&&n.push({type:"oauth",id:i.provider,name:i.provider});return n}function _e(e=""){let[n,i]=useState([]),[l,d]=useState(true),[g,f]=useState(null),o=useCallback(async()=>{try{let s=await fetch(`${e}/api/billing/plans`);if(s.ok){let a=await s.json();i(a.plans||a||[]);}}catch(s){f(s.message);}finally{d(false);}},[e]);return useEffect(()=>{o();},[o]),{plans:n,loading:l,error:g,refresh:o}}function We(e=""){let[n,i]=useState(null),[l,d]=useState(true),[g,f]=useState(null),o=useCallback(async()=>{try{let c=await fetch(`${e}/api/billing/subscription`);if(c.ok){let u=await c.json();i(u.plan?u:null);}}catch(c){f(c.message);}finally{d(false);}},[e]);useEffect(()=>{o();},[o]);let s=useCallback(async c=>{let u=x(),t=await(await fetch(`${e}/api/billing/subscribe`,{method:"POST",headers:{"Content-Type":"application/json",...u?{"x-csrf-token":u}:{}},body:JSON.stringify({planId:c})})).json();return t.url||t.checkoutUrl||null},[e]),a=useCallback(async()=>{let c=x();return (await fetch(`${e}/api/billing/cancel`,{method:"POST",headers:c?{"x-csrf-token":c}:{}})).ok?(await o(),true):false},[e,o]);return {subscription:n,loading:l,error:g,refresh:o,subscribe:s,cancel:a}}function Me(e=""){let[n,i]=useState(null),[l,d]=useState(true),[g,f]=useState(null),o=useCallback(async()=>{try{let s=await fetch(`${e}/api/billing/usage`);if(s.ok){let a=await s.json();i(a);}}catch(s){f(s.message);}finally{d(false);}},[e]);return useEffect(()=>{o();},[o]),{usage:n,loading:l,error:g,refresh:o}}function je(e=""){let[n,i]=useState(false),[l,d]=useState(null),g=useCallback(async o=>{i(true),d(null);try{let s=x(),a=await fetch(`${e}/api/billing/prepaid`,{method:"POST",headers:{"Content-Type":"application/json",...s?{"x-csrf-token":s}:{}},body:JSON.stringify({amountCents:o})}),c=await a.json();return a.ok?c.url||null:(d(c.error||"Purchase failed"),null)}catch(s){return d(s.message),null}finally{i(false);}},[e]),f=useCallback(async o=>{i(true),d(null);try{let s=x(),a=await fetch(`${e}/api/billing/verify-prepaid`,{method:"POST",headers:{"Content-Type":"application/json",...s?{"x-csrf-token":s}:{}},body:JSON.stringify({sessionId:o})}),c=await a.json();return a.ok?c:(d(c.error||"Verification failed"),null)}catch(s){return d(s.message),null}finally{i(false);}},[e]);return {purchase:g,verifySession:f,loading:n,error:l}}function Be(e="",n){let[i,l]=useState([]),[d,g]=useState(true),[f,o]=useState(null),s=n?.limit||50,a=n?.offset||0,c=useCallback(async()=>{try{let u=await fetch(`${e}/api/billing/history?limit=${s}&offset=${a}`);if(u.ok){let m=await u.json();l(m.records||m.history||(Array.isArray(m)?m:[]));}}catch(u){o(u.message);}finally{g(false);}},[e,s,a]);return useEffect(()=>{c();},[c]),{records:i,loading:d,error:f,refresh:c}}function De(){if(typeof document>"u")return null;let e=document.cookie.split(";").map(n=>n.trim()).find(n=>n.startsWith("__csrf="));return e?e.slice(7):null}function He(e,n){let[i,l]=useState(null),[d,g]=useState(true),[f,o]=useState(false),[s,a]=useState(null),c=n?.apiBaseUrl??"",u=n?.scope===void 0||n?.scope==="global"?"global":`stack:${n.scope.stackId}`,m=useCallback(r=>{let h=encodeURIComponent(r);if(u==="global")return `${c}/api/user/profile/${h}`;let y=u.slice(6);return `${c}/api/v2/stacks/${encodeURIComponent(y)}/members/${h}/profile`},[c,u]),t=useCallback(async()=>{if(!e){l(null),g(false);return}g(true),a(null);try{let r=await fetch(m(e));if(r.ok){let h=await r.json(),y=h.profile||h.data?.profile||h;l({mid:y.mid||e,username:y.username||"",avatarUrl:y.avatar_url||y.avatarUrl,bio:y.bio,paymentAddress:y.payment_address||y.paymentAddress,createdAt:y.created_at||y.createdAt,updatedAt:y.updated_at||y.updatedAt});}else if(r.status===404)l({mid:e,username:""});else throw new Error(`${r.status}`)}catch(r){a(r instanceof Error?r.message:"Failed to load profile");}finally{g(false);}},[e,m]);useEffect(()=>{t();},[t]);let p=useCallback(async r=>{if(!e)return false;o(true),a(null);try{let h=De(),y={};r.username!==void 0&&(y.username=r.username),r.avatarUrl!==void 0&&(y.avatar_url=r.avatarUrl),r.bio!==void 0&&(y.bio=r.bio),r.paymentAddress!==void 0&&(y.payment_address=r.paymentAddress);let w=await fetch(m(e),{method:"PUT",headers:{"Content-Type":"application/json",...h?{"x-csrf-token":h}:{}},credentials:"same-origin",body:JSON.stringify(y)});if(!w.ok){let S=await w.json().catch(()=>({}));throw new Error(S.error||S.message||`Update failed: ${w.status}`)}let A=await w.json(),C=A.profile||A.data?.profile||A;return l(S=>({mid:S?.mid||e,username:r.username??S?.username??"",avatarUrl:r.avatarUrl??S?.avatarUrl,bio:r.bio??S?.bio,paymentAddress:r.paymentAddress??C.payment_address??C.paymentAddress??S?.paymentAddress,createdAt:S?.createdAt,updatedAt:C.updated_at||C.updatedAt||Date.now()})),!0}catch(h){return a(h instanceof Error?h.message:"Update failed"),false}finally{o(false);}},[e,m]);return {profile:i,loading:d,saving:f,error:s,updateProfile:p,refresh:t}}var be="google-identity-services",Ke="https://accounts.google.com/gsi/client";function Ve({stackId:e,stacknetUrl:n="https://stacknet.magma-rpc.com",apiBaseUrl:i="",autoPrompt:l=true,cancelOnTapOutside:d=true,onSuccess:g,onError:f,disabled:o=false}){let{config:s}=Y(e,n),{isAuthenticated:a,loading:c,readSession:u}=L(),[m,t]=useState(false),[p,r]=useState(null),[h,y]=useState(false),w=useRef(false),A=useRef(false),S=s?.oauthProviders?.find(b=>b.provider==="google"&&b.enabled&&b.clientId)?.clientId||null;useEffect(()=>{if(o||!S||typeof window>"u")return;if(document.getElementById(be)){y(true);return}let b=document.createElement("script");b.id=be,b.src=Ke,b.async=true,b.defer=true,b.onload=()=>y(true),b.onerror=()=>{r("Failed to load Google sign-in"),f?.("Failed to load Google Identity Services script");},document.head.appendChild(b);},[o,S,f]);let E=useCallback(async b=>{t(true),r(null);try{let I=await fetch(`${i}/api/auth/google/one-tap`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({credential:b.credential,stackId:e})});if(!I.ok){let j=(await I.json().catch(()=>({}))).error||"Google sign-in failed";r(j),f?.(j),t(!1);return}u(),t(!1),g?.();}catch(I){let N=I.message||"Google sign-in failed";r(N),f?.(N),t(false);}},[i,e,u,g,f]);useEffect(()=>{if(o){console.debug("[GoogleOneTap] Disabled");return}if(!h){console.debug("[GoogleOneTap] Script not loaded yet, clientId:",S);return}if(!S){console.debug("[GoogleOneTap] No Google clientId from stack config");return}if(c){console.debug("[GoogleOneTap] Session still loading");return}if(a){console.debug("[GoogleOneTap] User already authenticated, skipping");return}if(!window.google?.accounts?.id){console.debug("[GoogleOneTap] GIS library not available on window");return}w.current||(console.debug("[GoogleOneTap] Initializing with clientId:",S),w.current=true,window.google.accounts.id.initialize({client_id:S,callback:E,auto_select:true,cancel_on_tap_outside:d}),l&&!A.current&&(A.current=true,console.debug("[GoogleOneTap] Showing prompt..."),window.google.accounts.id.prompt(b=>{b.isDisplayed?.()&&console.debug("[GoogleOneTap] Prompt displayed"),b.isNotDisplayed?.()&&console.debug("[GoogleOneTap] Not displayed:",b.getNotDisplayedReason?.()),b.isSkippedMoment?.()&&console.debug("[GoogleOneTap] Skipped:",b.getSkippedReason?.()),b.isDismissedMoment?.()&&console.debug("[GoogleOneTap] Dismissed:",b.getDismissedReason?.());})));},[o,h,S,c,a,E,l,d]),useEffect(()=>()=>{window.google?.accounts?.id&&w.current&&window.google.accounts.id.cancel();},[]);let $=useCallback(()=>{!window.google?.accounts?.id||!w.current||window.google.accounts.id.prompt();},[]),W=useCallback((b,I)=>{!b||!window.google?.accounts?.id||!w.current||window.google.accounts.id.renderButton(b,{theme:I?.theme||"filled_black",size:I?.size||"large",text:I?.text||"signin_with",width:I?.width});},[]);return {available:!!S,ready:h&&!!S,loading:m,error:p,prompt:$,renderButton:W,clientId:S}}var qe="/api";function Qe(e,n){return `${(n||(typeof window<"u"?window.location.origin:"")).replace(/\/$/,"")}/?ref=${encodeURIComponent(e)}`}function Xe(e={}){let{shareBaseUrl:n,autoMint:i=true}=e,[l,d]=useState(null),[g,f]=useState(true),[o,s]=useState(null),a=useCallback(async()=>{f(true),s(null);try{let m=i?"POST":"GET",t=await fetch(`${qe}/social/join-code`,{method:m,credentials:"include"});if(!t.ok){d(null),t.status===401?s("not_authenticated"):s(`HTTP ${t.status}`);return}let p=await t.json();d(p?.code??null);}catch(m){s(m?.message||"network_error"),d(null);}finally{f(false);}},[i]);useEffect(()=>{a();},[a]);let c=l?Qe(l,n):null,u=useCallback(async()=>{if(!c)return false;try{if(typeof navigator<"u"&&navigator.clipboard?.writeText)return await navigator.clipboard.writeText(c),!0}catch{}return false},[c]);return {code:l,shareUrl:c,loading:g,error:o,refresh:a,copyShareLink:u}}
2
- export{q as useAuthBridge,Be as useBillingHistory,K as useCSRFToken,Ve as useGoogleOneTap,Xe as useJoinCode,_e as usePlans,je as usePrepaidCheckout,He as useProfile,L as useSession,Te as useStackAuth,Y as useStackConfig,We as useSubscription,Me as useUsage,V as useWeb3Wallet};
1
+ import {useState,useCallback,useEffect,useRef,useMemo}from'react';function de(){if(typeof document>"u")return null;try{let e=document.cookie.split(";").map(i=>i.trim()).find(i=>i.startsWith("stackauth_session="));if(!e)return null;let n=e.slice(18);return JSON.parse(atob(n.replace(/-/g,"+").replace(/_/g,"/")))}catch{return null}}function O(e="__csrf"){if(typeof document>"u")return null;let n=document.cookie.split(";").map(i=>i.trim()).find(i=>i.startsWith(`${e}=`));return n?n.slice(e.length+1):null}function W(){let[e,n]=useState(null),[i,l]=useState(true),d=useCallback(()=>{let s=de();s&&s.expiresAt>Date.now()?n({userId:s.userId,address:s.address,chain:s.chain,expiresAt:s.expiresAt,planId:s.planId,authMethod:s.authMethod}):n(null),l(false);},[]);useEffect(()=>{d();},[d]);let p=useCallback(async(s="")=>{try{let o=await fetch(`${s}/api/auth/session`);if(o.ok){let a=await o.json();if(a.session)return n(a.session),a.session}return n(null),null}catch{return null}},[]),m=!!e&&e.expiresAt>Date.now();return {session:e,loading:i,isAuthenticated:m,refresh:p,readSession:d}}function z(e="__csrf",n="x-csrf-token"){let[i,l]=useState(null);useEffect(()=>{l(O(e));},[e]);let d=i?{[n]:i}:{};return {token:i,headers:d}}function q(){let[e,n]=useState({connected:false,address:null,chain:null,provider:null}),[i,l]=useState(null),d=useCallback(async(o="phantom")=>{l(null);try{let a=typeof window<"u"?window:null,u=o==="phantom"?a?.phantom?.solana||a?.solana:a?.solflare;if(!u)return l(`${o} wallet not found`),null;let y=(await u.connect()).publicKey.toString();return n({connected:!0,address:y,chain:"solana",provider:o}),y}catch(a){return l(a.message||"Failed to connect wallet"),null}},[]),p=useCallback(async()=>{l(null);try{let a=(typeof window<"u"?window:null)?.ethereum;if(!a)return l("MetaMask not found"),null;let u=a;a.providers?.length&&(u=a.providers.find(t=>t.isMetaMask)||a);let y=(await u.request({method:"eth_requestAccounts"}))[0];return y?(n({connected:!0,address:y,chain:"ethereum",provider:"metamask"}),y):(l("No account selected"),null)}catch(o){return l(o.message||"Failed to connect wallet"),null}},[]),m=useCallback(async(o,a)=>{l(null);let u=a?.chain||e.chain,c=a?.provider||e.provider,y=a?.address||e.address;try{if(u==="solana"){let t=typeof window<"u"?window:null,h=c==="solflare"?t?.solflare:t?.phantom?.solana||t?.solana;if(!h)throw new Error("Wallet not available");let r=new TextEncoder().encode(o),b=await h.signMessage(r,"utf8"),f=new Uint8Array(b.signature||b),g="";for(let I=0;I<f.byteLength;I++)g+=String.fromCharCode(f[I]);return btoa(g)}if(u==="ethereum"){let h=(typeof window<"u"?window:null)?.ethereum;if(h?.providers?.length&&(h=h.providers.find(b=>b.isMetaMask)||h),!h)throw new Error("MetaMask not available");return await h.request({method:"personal_sign",params:[o,y]})}throw new Error("No wallet connected")}catch(t){return l(t.message||"Signing failed"),null}},[e]),s=useCallback(()=>{n({connected:false,address:null,chain:null,provider:null}),l(null);},[]);return {wallet:e,error:i,connectSolana:d,connectEVM:p,signMessage:m,disconnect:s}}var Te="https://stacknet.magma-rpc.com/auth/bridge",H="stacknet-auth-bridge";function X(e){let n=e?.bridgeUrl||Te,i=e?.disabled||false,l=useRef(null),[d,p]=useState({ready:false,known:false,identity:null,identityCount:0,resolvedStackId:null}),m=useRef([]),s=useRef(false),o=useCallback(t=>{let h={...t,protocol:H};s.current&&l.current?.contentWindow?l.current.contentWindow.postMessage(h,new URL(n).origin):m.current.push(h);},[n]);useEffect(()=>{if(i)return;let t=r=>{if(!(!r.data||r.data.protocol!==H)){try{if(r.origin!==new URL(n).origin)return}catch{return}switch(r.data.type){case "bridge:ready":s.current=true,p(b=>({...b,ready:true}));for(let b of m.current)l.current?.contentWindow?.postMessage(b,r.origin);m.current=[],l.current?.contentWindow?.postMessage({protocol:H,type:"auth:check"},r.origin),l.current?.contentWindow?.postMessage({protocol:H,type:"auth:resolve-stack"},r.origin);break;case "auth:status":p(b=>({...b,known:r.data.known,identity:r.data.identity,identityCount:r.data.identityCount||0}));break;case "auth:resolved-stack":p(b=>({...b,resolvedStackId:r.data.stackId||null}));break;}}};window.addEventListener("message",t);let h=document.createElement("iframe");return h.src=n,h.style.display="none",h.setAttribute("aria-hidden","true"),h.setAttribute("tabindex","-1"),h.setAttribute("sandbox","allow-scripts allow-same-origin"),document.body.appendChild(h),l.current=h,()=>{window.removeEventListener("message",t),h.parentNode&&h.parentNode.removeChild(h),l.current=null,s.current=false;}},[n,i]);let a=useCallback(t=>{o({type:"auth:connected",...t});},[o]),u=useCallback(t=>{o({type:"auth:disconnected",...t});},[o]),c=useCallback(()=>{o({type:"auth:clear"}),p({ready:d.ready,known:false,identity:null,identityCount:0,resolvedStackId:null});},[o,d.ready]),y=useCallback(()=>{o({type:"auth:check"});},[o]);return {...d,reportConnected:a,reportDisconnected:u,clearAll:c,refresh:y}}async function me(e,n,i,l){let d=e.apiVersion||"v2",p=`${e.baseUrl}/api/${d}${i}`;try{let m=await fetch(p,{method:n,headers:{"Content-Type":"application/json"},body:l?JSON.stringify(l):void 0}),s=await m.json();return m.ok?s.success&&s.data!==void 0?{success:!0,data:s.data}:{success:!0,data:s}:{success:!1,error:s.error||{code:"UNKNOWN_ERROR",message:"Unknown error"}}}catch(m){return {success:false,error:{code:"NETWORK_ERROR",message:m instanceof Error?m.message:"Network error"}}}}function he(e){return {getNetworkStatus:()=>me(e,"GET","/network/status"),getWeb3Challenge:(n,i)=>me(e,"POST",`/stacks/${encodeURIComponent(e.stackId)}/auth/web3/challenge`,{chain:n,address:i})}}function Re(e={apiBaseUrl:""}){let{wallet:n,connectSolana:i,connectEVM:l,signMessage:d,disconnect:p}=q(),{session:m,isAuthenticated:s,refresh:o,readSession:a}=W(),{headers:u}=z(),c=X({disabled:typeof window>"u"}),[y,t]=useState(false),[h,r]=useState(null),[b,f]=useState(false),g=e.apiBaseUrl||"",I=e.stacknetUrl||"https://stacknet.magma-rpc.com",C=e.stackId||c.resolvedStackId||"",S=he({baseUrl:I,stackId:C}),_=useCallback(async(k,P,T,R)=>{t(true),r(null);try{let v=P;if(!v){let F=await T();if(!F)return t(!1),!1;v=F;}let U=await S.getWeb3Challenge(k,v);if(!U.success||!U.data)return r("Failed to get challenge"),t(!1),!1;let x=await d(U.data.message,{chain:k,provider:R,address:v});if(!x)return t(!1),!1;let B={chain:k,message:U.data.message,signature:x,stackId:C};k==="solana"&&(B.publicKey=v);let J=await fetch(`${g}/api/auth/callback`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify(B)});if(!J.ok){let F=await J.json().catch(()=>({}));return r(F.error||"Authentication failed"),t(!1),!1}return c.reportConnected({address:v,chain:k,method:R||(k==="solana"?"phantom":"metamask"),stackId:C}),a(),t(!1),!0}catch(v){return r(v.message||"Authentication failed"),t(false),false}},[g,S,d,a,c,C]),$=useCallback(async(k="phantom")=>{let P=await i(k);return P?_("solana",P,()=>i(k),k):false},[i,_]),L=useCallback(async()=>{let k=await l();return k?_("ethereum",k,l,"metamask"):false},[l,_]),w=useCallback(async k=>{t(true),r(null);try{let P=await fetch(`${g}/api/auth/otp`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify({code:k})});if(!P.ok){let T=await P.json().catch(()=>({}));return r(T.error||"Invalid code"),t(!1),!1}return a(),t(!1),!0}catch(P){return r(P.message||"OTP verification failed"),t(false),false}},[g,a]),A=useCallback(async(k,P)=>{t(true),r(null);try{let T=P||`${window.location.origin}/api/auth/oauth/callback`,R=new URLSearchParams({provider:k,redirectUri:T,stackId:C}),v=await fetch(`${g}/api/auth/oauth/start?${R}`,{credentials:"include"});if(!v.ok){let x=await v.json().catch(()=>({}));return r(x.error||"Failed to start OAuth flow"),t(!1),!1}let U=await v.json();if(U.redirect_url){let x;try{x=new URL(U.redirect_url);}catch{return r("Invalid OAuth redirect URL"),t(!1),!1}let B=[/(^|\.)accounts\.google\.com$/,/(^|\.)discord\.com$/,/(^|\.)github\.com$/,/(^|\.)x\.com$/,/(^|\.)twitter\.com$/,/(^|\.)apple\.com$/];return x.protocol!=="https:"||!B.some(J=>J.test(x.hostname))?(r(`Refusing to redirect to non-OAuth host: ${x.hostname}`),t(!1),!1):(typeof sessionStorage<"u"&&(sessionStorage.setItem("oauth_state",U.state||""),sessionStorage.setItem("oauth_provider",k)),window.location.href=x.toString(),!0)}return r("No redirect URL returned"),t(!1),!1}catch(T){return r(T.message||"OAuth flow failed"),t(false),false}},[g,C]),M=useCallback(async(k,P,T)=>{t(true),r(null);try{if(typeof sessionStorage<"u"){let v=sessionStorage.getItem("oauth_state"),U=sessionStorage.getItem("oauth_provider");if(sessionStorage.removeItem("oauth_state"),sessionStorage.removeItem("oauth_provider"),!v||v!==T)return r("OAuth state mismatch \u2014 refusing to complete login"),t(!1),!1;if(U&&U!==k)return r("OAuth provider mismatch \u2014 refusing to complete login"),t(!1),!1}let R=await fetch(`${g}/api/auth/oauth/callback`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify({provider:k,code:P,state:T,stackId:C})});if(!R.ok){let v=await R.json().catch(()=>({}));return r(v.error||"OAuth authentication failed"),t(!1),!1}return a(),t(!1),!0}catch(R){return r(R.message||"OAuth callback failed"),t(false),false}},[g,C,a]),G=useCallback(async()=>{n.address&&n.chain&&c.reportDisconnected({address:n.address,chain:n.chain,stackId:C});try{await fetch(`${g}/api/auth/logout`,{method:"POST",headers:u,credentials:"include"});}catch{}p(),a();},[g,u,p,a,n,c,C]);return useEffect(()=>{if(!e.autoConnect||b||s||!c.ready||!c.known||!c.identity)return;f(true);let{chain:k,method:P}=c.identity;k==="solana"&&(P==="phantom"||P==="solflare")?$(P):k==="ethereum"&&L();},[e.autoConnect,b,s,c,$,L]),{session:m,isAuthenticated:s,wallet:n,loading:y,error:h,authenticateSolana:$,authenticateEVM:L,authenticateOTP:w,authenticateOAuth:A,authenticateOAuthCallback:M,logout:G,refresh:()=>o(g),stackId:C,bridge:{ready:c.ready,known:c.known,identity:c.identity,identityCount:c.identityCount,resolvedStackId:c.resolvedStackId}}}function ee(e,n="https://stacknet.magma-rpc.com"){let[i,l]=useState(null),[d,p]=useState(false),[m,s]=useState(null),o=useCallback(async u=>{p(true),s(null);try{let c=await fetch(`${n}/api/v2/stacks/${u}`);if(!c.ok)return s("Stack not found"),p(!1),null;let y=await c.json(),t=y.data?.stack||y.stack||y,h={id:t.id,name:t.name,displayName:t.displayName||t.name,description:t.description,logoUrl:t.logoUrl,webPageUrl:t.webPageUrl,allowedChains:t.allowedChains||[],features:t.features,stripeProvider:t.stripeProvider,oauthProviders:t.oauthProviders?.map(r=>({provider:r.provider,clientId:r.clientId,enabled:r.enabled!==!1}))};return l(h),p(!1),h}catch(c){return s(c.message),p(false),null}},[n]);useEffect(()=>{e&&o(e);},[e,o]);let a=i?Ee(i):[];return {config:i,loading:d,error:m,identityProviders:a,fetchConfig:o}}function Ee(e){let n=[];if(e.features?.web3Auth!==false&&(e.allowedChains.includes("solana")&&(n.push({type:"wallet",id:"phantom",name:"Phantom",chain:"solana"}),n.push({type:"wallet",id:"solflare",name:"Solflare",chain:"solana"})),(e.allowedChains.includes("ethereum")||e.allowedChains.includes("polygon")||e.allowedChains.includes("base"))&&n.push({type:"wallet",id:"metamask",name:"MetaMask",chain:"ethereum"})),e.features?.apiKeyAuth!==false&&n.push({type:"otp",id:"otp",name:"Access Code"}),e.features?.oauthAuth&&e.oauthProviders)for(let i of e.oauthProviders)i.enabled&&n.push({type:"oauth",id:i.provider,name:i.provider});return n}function Me(e=""){let[n,i]=useState([]),[l,d]=useState(true),[p,m]=useState(null),s=useCallback(async()=>{try{let o=await fetch(`${e}/api/billing/plans`);if(o.ok){let a=await o.json();i(a.plans||a||[]);}}catch(o){m(o.message);}finally{d(false);}},[e]);return useEffect(()=>{s();},[s]),{plans:n,loading:l,error:p,refresh:s}}function Ne(e=""){let[n,i]=useState(null),[l,d]=useState(true),[p,m]=useState(null),s=useCallback(async()=>{try{let u=await fetch(`${e}/api/billing/subscription`);if(u.ok){let c=await u.json();i(c.plan?c:null);}}catch(u){m(u.message);}finally{d(false);}},[e]);useEffect(()=>{s();},[s]);let o=useCallback(async u=>{let c=O(),t=await(await fetch(`${e}/api/billing/subscribe`,{method:"POST",headers:{"Content-Type":"application/json",...c?{"x-csrf-token":c}:{}},body:JSON.stringify({planId:u})})).json();return t.url||t.checkoutUrl||null},[e]),a=useCallback(async()=>{let u=O();return (await fetch(`${e}/api/billing/cancel`,{method:"POST",headers:u?{"x-csrf-token":u}:{}})).ok?(await s(),true):false},[e,s]);return {subscription:n,loading:l,error:p,refresh:s,subscribe:o,cancel:a}}function Be(e=""){let[n,i]=useState(null),[l,d]=useState(true),[p,m]=useState(null),s=useCallback(async()=>{try{let o=await fetch(`${e}/api/billing/usage`);if(o.ok){let a=await o.json();i(a);}}catch(o){m(o.message);}finally{d(false);}},[e]);return useEffect(()=>{s();},[s]),{usage:n,loading:l,error:p,refresh:s}}function Je(e=""){let[n,i]=useState(false),[l,d]=useState(null),p=useCallback(async s=>{i(true),d(null);try{let o=O(),a=await fetch(`${e}/api/billing/prepaid`,{method:"POST",headers:{"Content-Type":"application/json",...o?{"x-csrf-token":o}:{}},body:JSON.stringify({amountCents:s})}),u=await a.json();return a.ok?u.url||null:(d(u.error||"Purchase failed"),null)}catch(o){return d(o.message),null}finally{i(false);}},[e]),m=useCallback(async s=>{i(true),d(null);try{let o=O(),a=await fetch(`${e}/api/billing/verify-prepaid`,{method:"POST",headers:{"Content-Type":"application/json",...o?{"x-csrf-token":o}:{}},body:JSON.stringify({sessionId:s})}),u=await a.json();return a.ok?u:(d(u.error||"Verification failed"),null)}catch(o){return d(o.message),null}finally{i(false);}},[e]);return {purchase:p,verifySession:m,loading:n,error:l}}function He(e="",n){let[i,l]=useState([]),[d,p]=useState(true),[m,s]=useState(null),o=n?.limit||50,a=n?.offset||0,u=useCallback(async()=>{try{let c=await fetch(`${e}/api/billing/history?limit=${o}&offset=${a}`);if(c.ok){let y=await c.json();l(y.records||y.history||(Array.isArray(y)?y:[]));}}catch(c){s(c.message);}finally{p(false);}},[e,o,a]);return useEffect(()=>{u();},[u]),{records:i,loading:d,error:m,refresh:u}}function Ve(){if(typeof document>"u")return null;let e=document.cookie.split(";").map(n=>n.trim()).find(n=>n.startsWith("__csrf="));return e?e.slice(7):null}function ze(e,n){let[i,l]=useState(null),[d,p]=useState(true),[m,s]=useState(false),[o,a]=useState(null),u=n?.apiBaseUrl??"",c=n?.scope===void 0||n?.scope==="global"?"global":`stack:${n.scope.stackId}`,y=useCallback(r=>{let b=encodeURIComponent(r);if(c==="global")return `${u}/api/user/profile/${b}`;let f=c.slice(6);return `${u}/api/v2/stacks/${encodeURIComponent(f)}/members/${b}/profile`},[u,c]),t=useCallback(async()=>{if(!e){l(null),p(false);return}p(true),a(null);try{let r=await fetch(y(e));if(r.ok){let b=await r.json(),f=b.profile||b.data?.profile||b;l({mid:f.mid||e,username:f.username||"",avatarUrl:f.avatar_url||f.avatarUrl,bio:f.bio,paymentAddress:f.payment_address||f.paymentAddress,createdAt:f.created_at||f.createdAt,updatedAt:f.updated_at||f.updatedAt});}else if(r.status===404)l({mid:e,username:""});else throw new Error(`${r.status}`)}catch(r){a(r instanceof Error?r.message:"Failed to load profile");}finally{p(false);}},[e,y]);useEffect(()=>{t();},[t]);let h=useCallback(async r=>{if(!e)return false;s(true),a(null);try{let b=Ve(),f={};r.username!==void 0&&(f.username=r.username),r.avatarUrl!==void 0&&(f.avatar_url=r.avatarUrl),r.bio!==void 0&&(f.bio=r.bio),r.paymentAddress!==void 0&&(f.payment_address=r.paymentAddress);let g=await fetch(y(e),{method:"PUT",headers:{"Content-Type":"application/json",...b?{"x-csrf-token":b}:{}},credentials:"same-origin",body:JSON.stringify(f)});if(!g.ok){let S=await g.json().catch(()=>({}));throw new Error(S.error||S.message||`Update failed: ${g.status}`)}let I=await g.json(),C=I.profile||I.data?.profile||I;return l(S=>({mid:S?.mid||e,username:r.username??S?.username??"",avatarUrl:r.avatarUrl??S?.avatarUrl,bio:r.bio??S?.bio,paymentAddress:r.paymentAddress??C.payment_address??C.paymentAddress??S?.paymentAddress,createdAt:S?.createdAt,updatedAt:C.updated_at||C.updatedAt||Date.now()})),!0}catch(b){return a(b instanceof Error?b.message:"Update failed"),false}finally{s(false);}},[e,y]);return {profile:i,loading:d,saving:m,error:o,updateProfile:h,refresh:t}}var ke="google-identity-services",qe="https://accounts.google.com/gsi/client";function Qe({stackId:e,stacknetUrl:n="https://stacknet.magma-rpc.com",apiBaseUrl:i="",autoPrompt:l=true,cancelOnTapOutside:d=true,onSuccess:p,onError:m,disabled:s=false}){let{config:o}=ee(e,n),{isAuthenticated:a,loading:u,readSession:c}=W(),[y,t]=useState(false),[h,r]=useState(null),[b,f]=useState(false),g=useRef(false),I=useRef(false),S=o?.oauthProviders?.find(w=>w.provider==="google"&&w.enabled&&w.clientId)?.clientId||null;useEffect(()=>{if(s||!S||typeof window>"u")return;if(document.getElementById(ke)){f(true);return}let w=document.createElement("script");w.id=ke,w.src=qe,w.async=true,w.defer=true,w.onload=()=>f(true),w.onerror=()=>{r("Failed to load Google sign-in"),m?.("Failed to load Google Identity Services script");},document.head.appendChild(w);},[s,S,m]);let _=useCallback(async w=>{t(true),r(null);try{let A=await fetch(`${i}/api/auth/google/one-tap`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({credential:w.credential,stackId:e})});if(!A.ok){let G=(await A.json().catch(()=>({}))).error||"Google sign-in failed";r(G),m?.(G),t(!1);return}c(),t(!1),p?.();}catch(A){let M=A.message||"Google sign-in failed";r(M),m?.(M),t(false);}},[i,e,c,p,m]);useEffect(()=>{if(s){console.debug("[GoogleOneTap] Disabled");return}if(!b){console.debug("[GoogleOneTap] Script not loaded yet, clientId:",S);return}if(!S){console.debug("[GoogleOneTap] No Google clientId from stack config");return}if(u){console.debug("[GoogleOneTap] Session still loading");return}if(a){console.debug("[GoogleOneTap] User already authenticated, skipping");return}if(!window.google?.accounts?.id){console.debug("[GoogleOneTap] GIS library not available on window");return}g.current||(console.debug("[GoogleOneTap] Initializing with clientId:",S),g.current=true,window.google.accounts.id.initialize({client_id:S,callback:_,auto_select:true,cancel_on_tap_outside:d}),l&&!I.current&&(I.current=true,console.debug("[GoogleOneTap] Showing prompt..."),window.google.accounts.id.prompt(w=>{w.isDisplayed?.()&&console.debug("[GoogleOneTap] Prompt displayed"),w.isNotDisplayed?.()&&console.debug("[GoogleOneTap] Not displayed:",w.getNotDisplayedReason?.()),w.isSkippedMoment?.()&&console.debug("[GoogleOneTap] Skipped:",w.getSkippedReason?.()),w.isDismissedMoment?.()&&console.debug("[GoogleOneTap] Dismissed:",w.getDismissedReason?.());})));},[s,b,S,u,a,_,l,d]),useEffect(()=>()=>{window.google?.accounts?.id&&g.current&&window.google.accounts.id.cancel();},[]);let $=useCallback(()=>{!window.google?.accounts?.id||!g.current||window.google.accounts.id.prompt();},[]),L=useCallback((w,A)=>{!w||!window.google?.accounts?.id||!g.current||window.google.accounts.id.renderButton(w,{theme:A?.theme||"filled_black",size:A?.size||"large",text:A?.text||"signin_with",width:A?.width});},[]);return {available:!!S,ready:b&&!!S,loading:y,error:h,prompt:$,renderButton:L,clientId:S}}var Ye="/api";function Ze(e,n){return `${(n||(typeof window<"u"?window.location.origin:"")).replace(/\/$/,"")}/?ref=${encodeURIComponent(e)}`}function et(e={}){let{shareBaseUrl:n,autoMint:i=true}=e,[l,d]=useState(null),[p,m]=useState(true),[s,o]=useState(null),a=useCallback(async()=>{m(true),o(null);try{let y=i?"POST":"GET",t=await fetch(`${Ye}/social/join-code`,{method:y,credentials:"include"});if(!t.ok){d(null),t.status===401?o("not_authenticated"):o(`HTTP ${t.status}`);return}let h=await t.json();d(h?.code??null);}catch(y){o(y?.message||"network_error"),d(null);}finally{m(false);}},[i]);useEffect(()=>{a();},[a]);let u=l?Ze(l,n):null,c=useCallback(async()=>{if(!u)return false;try{if(typeof navigator<"u"&&navigator.clipboard?.writeText)return await navigator.clipboard.writeText(u),!0}catch{}return false},[u]);return {code:l,shareUrl:u,loading:p,error:s,refresh:a,copyShareLink:c}}var Ce="/api";function rt(e={}){let{shareBaseUrl:n}=e,[i,l]=useState([]),[d,p]=useState(null),[m,s]=useState(true),[o,a]=useState(false),[u,c]=useState(null),y=useCallback(async()=>{s(true),c(null);try{let f=await fetch(`${Ce}/social/invites`,{method:"GET",credentials:"include"});if(!f.ok){f.status===401?c("not_authenticated"):c(`HTTP ${f.status}`),l([]),p(null);return}let g=await f.json();l(Array.isArray(g.codes)?g.codes:[]),p(typeof g.limit=="number"?g.limit:null);}catch(f){c(f?.message||"network_error"),l([]),p(null);}finally{s(false);}},[]);useEffect(()=>{y();},[y]);let t=useCallback(async()=>{a(true),c(null);try{let f=await fetch(`${Ce}/social/invites`,{method:"POST",credentials:"include"}),g=await f.json().catch(()=>({}));return f.ok?(Array.isArray(g.codes)&&l(g.codes),typeof g.limit=="number"&&p(g.limit),g.minted??null):(f.status===409&&g.error==="code_limit_reached"?c("code_limit_reached"):f.status===401?c("not_authenticated"):c(g.error||`HTTP ${f.status}`),null)}catch(f){return c(f?.message||"network_error"),null}finally{a(false);}},[]),h=useCallback(f=>{let g=n??(typeof window<"u"?window.location.origin:null);return g?`${g.replace(/\/$/,"")}/?ref=${encodeURIComponent(f)}`:null},[n]),r=useCallback(async f=>{let g=h(f);if(!g)return false;try{if(typeof navigator<"u"&&navigator.clipboard?.writeText)return await navigator.clipboard.writeText(g),!0}catch{}return false},[h]),b=useMemo(()=>d==null?null:Math.max(0,d-i.length),[d,i.length]);return {codes:i,limit:d,remaining:b,loading:m,minting:o,error:u,refresh:y,mint:t,buildShareUrl:h,copyShareLink:r}}
2
+ export{X as useAuthBridge,He as useBillingHistory,z as useCSRFToken,Qe as useGoogleOneTap,rt as useInvites,et as useJoinCode,Me as usePlans,Je as usePrepaidCheckout,ze as useProfile,W as useSession,Re as useStackAuth,ee as useStackConfig,Ne as useSubscription,Be as useUsage,q as useWeb3Wallet};