@stacknet/userutils 0.5.6 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
- import { b as AuthTransport } from '../types-CAoB_5kk.cjs';
2
- import '../auth-DR2aYcor.cjs';
1
+ import { b as AuthTransport } from '../types-B_Vj6cr4.cjs';
2
+ import '../auth-c1d7Eji2.cjs';
3
3
 
4
4
  /**
5
5
  * Web transport — uses HttpOnly cookies managed by the server.
@@ -1,5 +1,5 @@
1
- import { b as AuthTransport } from '../types-Dghy_8Wh.js';
2
- import '../auth-DR2aYcor.js';
1
+ import { b as AuthTransport } from '../types-Cu0do-w-.js';
2
+ import '../auth-c1d7Eji2.js';
3
3
 
4
4
  /**
5
5
  * Web transport — uses HttpOnly cookies managed by the server.
@@ -24,6 +24,12 @@ interface PublicSession {
24
24
  expiresAt: number;
25
25
  planId?: string;
26
26
  authMethod?: string;
27
+ /**
28
+ * The user's affiliate / invite code. Always present post-auth (issued
29
+ * lazily on first session), so consumer apps can render a share link.
30
+ * Format: 8-char base62 (excluding visually-ambiguous chars).
31
+ */
32
+ joinCode?: string;
27
33
  }
28
34
  interface MPCNode {
29
35
  id: string;
@@ -24,6 +24,12 @@ interface PublicSession {
24
24
  expiresAt: number;
25
25
  planId?: string;
26
26
  authMethod?: string;
27
+ /**
28
+ * The user's affiliate / invite code. Always present post-auth (issued
29
+ * lazily on first session), so consumer apps can render a share link.
30
+ * Format: 8-char base62 (excluding visually-ambiguous chars).
31
+ */
32
+ joinCode?: string;
27
33
  }
28
34
  interface MPCNode {
29
35
  id: string;
@@ -1,8 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode } from 'react';
3
- import { d as UserUtilsConfig, c as UserUtilsCallbacks } from '../config-Bjh8PEhY.cjs';
3
+ import { d as UserUtilsConfig, c as UserUtilsCallbacks } from '../config-CLzVWDrU.cjs';
4
4
  import { ProfileScope } from '../types/index.cjs';
5
- import '../auth-DR2aYcor.cjs';
5
+ import '../auth-c1d7Eji2.cjs';
6
6
 
7
7
  interface UserUtilsContextValue {
8
8
  config: UserUtilsConfig;
@@ -1,8 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode } from 'react';
3
- import { d as UserUtilsConfig, c as UserUtilsCallbacks } from '../config-_ZjAzNkJ.js';
3
+ import { d as UserUtilsConfig, c as UserUtilsCallbacks } from '../config-xNca5ufB.js';
4
4
  import { ProfileScope } from '../types/index.js';
5
- import '../auth-DR2aYcor.js';
5
+ import '../auth-c1d7Eji2.js';
6
6
 
7
7
  interface UserUtilsContextValue {
8
8
  config: UserUtilsConfig;
@@ -0,0 +1,177 @@
1
+ import { P as PublicSession } from './auth-c1d7Eji2.cjs';
2
+
3
+ interface BillingPlan {
4
+ id: string;
5
+ name: string;
6
+ price_cents: number;
7
+ token_allocation: number;
8
+ features: string;
9
+ sort_order?: number;
10
+ is_active?: boolean;
11
+ }
12
+ interface Subscription {
13
+ plan?: {
14
+ id: string;
15
+ name: string;
16
+ priceCents: number;
17
+ tokenAllocation: number;
18
+ };
19
+ planId?: string;
20
+ planName?: string;
21
+ status: string;
22
+ tokensUsed?: number;
23
+ tokensRemaining?: number;
24
+ usagePercent?: number;
25
+ periodStart?: number;
26
+ periodEnd?: number;
27
+ cancelAtPeriodEnd?: boolean;
28
+ stripeCustomerId?: string;
29
+ }
30
+ interface UsageSummary {
31
+ ownerId: string;
32
+ planAllocation: number;
33
+ planId: string | null;
34
+ planName: string;
35
+ inferenceUsed: number;
36
+ ledgerSpent: number;
37
+ totalUsed: number;
38
+ remaining: number;
39
+ percent: number;
40
+ exceeded: boolean;
41
+ breakdown: {
42
+ subscription: number;
43
+ prepaidCredit: number;
44
+ tokenGrants: number;
45
+ inference: number;
46
+ skillRegistrations: number;
47
+ skillRegistrationCount: number;
48
+ };
49
+ }
50
+ interface PrepaidCheckoutResult {
51
+ url: string;
52
+ sessionId?: string;
53
+ }
54
+ interface PrepaidVerifyResult {
55
+ alreadyCredited: boolean;
56
+ tokensCredited?: number;
57
+ amountCents?: number;
58
+ paymentRef?: string;
59
+ }
60
+ interface BillingRecord {
61
+ id: string;
62
+ recorded_at?: number;
63
+ date?: string;
64
+ reason?: string;
65
+ description?: string;
66
+ amount: number;
67
+ direction?: string;
68
+ source?: string;
69
+ status?: string;
70
+ payment_ref?: string;
71
+ }
72
+
73
+ /** Decode JWT payload without verification (server-side helper) */
74
+ declare function decodeJWTPayload(jwt: string): Record<string, any> | null;
75
+ /** Sign a JWT with HMAC-SHA256 */
76
+ declare function signJWT(payload: Record<string, any>, secret: string): string;
77
+ /** Verify a JWT signature with HMAC-SHA256 (constant-time comparison) */
78
+ declare function verifyJWTSignature(jwt: string, secret: string): boolean;
79
+ /** Verify JWT and return payload if valid (checks signature + expiry) */
80
+ declare function verifyJWT(jwt: string, secret: string): Record<string, any> | null;
81
+ /**
82
+ * Check if JWT needs refresh and return a new one if so.
83
+ * Returns null if no refresh needed or JWT is invalid.
84
+ */
85
+ declare function maybeRefreshJWT(jwt: string, secret: string, expirySeconds?: number, refreshWindowSeconds?: number): string | null;
86
+ /** Generate a cryptographically secure random token */
87
+ declare function generateToken(bytes?: number): string;
88
+ /** Options controlling how the real client IP is extracted from a Request.
89
+ *
90
+ * The previous implementation trusted `X-Forwarded-For[0]` unconditionally,
91
+ * which lets any direct caller pin arbitrary values and bypass per-IP rate
92
+ * limits. The correct read depends on how many reverse proxies sit between
93
+ * the app and the real client: with N trusted hops, the real client IP is
94
+ * at position `(length - N)` in the XFF list, because each trusted hop
95
+ * appends its peer IP and the attacker-controlled prefix is pushed left. */
96
+ interface IPExtractorConfig {
97
+ /** Number of trusted reverse-proxy hops between this app and the real client.
98
+ * - `0`: do NOT trust X-Forwarded-For (app is directly internet-exposed).
99
+ * - `1` (default): one trusted proxy (e.g. single LB, Vercel edge, Nginx).
100
+ * - `N`: N trusted hops; the real client IP is `XFF[length - N]`.
101
+ * If XFF has fewer entries than expected, the chain is misconfigured or
102
+ * spoofed and the extractor falls through to the next source. */
103
+ trustedProxyCount?: number;
104
+ /** Trust the `X-Real-IP` header. Only enable if your proxy sets it AND
105
+ * strips any inbound `X-Real-IP` from clients. Default: `false`. */
106
+ trustRealIpHeader?: boolean;
107
+ /** Override entirely — return the real client IP from a request however
108
+ * you know best (e.g. a platform-specific header like `cf-connecting-ip`
109
+ * or `x-vercel-forwarded-for`). If this returns a non-empty string it
110
+ * wins; if it returns null/empty the other strategies run. */
111
+ customExtractor?: (request: Request) => string | null | undefined;
112
+ }
113
+ /** Extract the real client IP address from a request.
114
+ *
115
+ * Default behavior (`trustedProxyCount: 1`) is safe for the common case of
116
+ * one trusted reverse proxy. Consumers with no proxy must pass `0`, and
117
+ * consumers behind multiple proxies must pass the exact hop count. */
118
+ declare function extractIP(request: Request, config?: IPExtractorConfig): string;
119
+
120
+ /** Client-side configuration for UserUtilsProvider */
121
+ interface UserUtilsConfig {
122
+ /** Base URL for API calls (empty string = same origin) */
123
+ apiBaseUrl: string;
124
+ /** StackNet stack identifier */
125
+ stackId?: string;
126
+ /** Direct StackNet URL for client-side API calls (e.g. challenges) */
127
+ stacknetUrl?: string;
128
+ /** Theme preference */
129
+ theme?: 'light' | 'dark' | 'system';
130
+ }
131
+ /** Callbacks for auth and billing events */
132
+ interface UserUtilsCallbacks {
133
+ onAuthSuccess?: (session: PublicSession) => void;
134
+ onAuthError?: (error: Error) => void;
135
+ onLogout?: () => void;
136
+ onSubscriptionChange?: (subscription: Subscription) => void;
137
+ }
138
+ /** Server-side configuration for handler factories */
139
+ interface ServerConfig {
140
+ /** HMAC-SHA256 secret for signing JWTs */
141
+ authSecret: string;
142
+ /** StackNet backend URL (always https://stacknet.magma-rpc.com) */
143
+ stacknetUrl: string;
144
+ /** StackNet stack identifier */
145
+ stackId: string;
146
+ /** JWT secret for re-signing to StackNet (defaults to authSecret) */
147
+ stacknetJwtSecret?: string;
148
+ /** Cookie domain for subdomain sharing (e.g. '.geoff.ai') */
149
+ cookieDomain?: string;
150
+ /** Use Secure flag on cookies (default: true) */
151
+ secureCookies?: boolean;
152
+ /** Session max age in seconds (default: 604800 = 7 days) */
153
+ sessionMaxAge?: number;
154
+ /** JWT expiry in seconds (default: 900 = 15 minutes) */
155
+ jwtExpiry?: number;
156
+ /**
157
+ * Google OAuth client ID (single). Used by createGoogleOneTapHandler to
158
+ * validate the `aud` claim on incoming Google ID tokens. Required for
159
+ * Google One Tap — without it, an ID token issued to any other Google
160
+ * application could be replayed against this endpoint.
161
+ */
162
+ googleClientId?: string;
163
+ /**
164
+ * Google OAuth client IDs (multiple). Use when the stack accepts tokens
165
+ * from more than one Google client (e.g. web + native).
166
+ */
167
+ googleClientIds?: string[];
168
+ /**
169
+ * How the handlers should derive the real client IP for rate-limit keys.
170
+ * Defaults to `{ trustedProxyCount: 1 }`. Set `trustedProxyCount: 0` if
171
+ * the app is exposed directly (no proxy) — otherwise any caller can pin
172
+ * their X-Forwarded-For and bypass rate limiting.
173
+ */
174
+ ipConfig?: IPExtractorConfig;
175
+ }
176
+
177
+ export { type BillingPlan as B, type IPExtractorConfig as I, type PrepaidCheckoutResult as P, type Subscription as S, type UsageSummary as U, type BillingRecord as a, type PrepaidVerifyResult as b, type UserUtilsCallbacks as c, type UserUtilsConfig as d, type ServerConfig as e, decodeJWTPayload as f, extractIP as g, generateToken as h, verifyJWTSignature as i, maybeRefreshJWT as m, signJWT as s, verifyJWT as v };
@@ -0,0 +1,177 @@
1
+ import { P as PublicSession } from './auth-c1d7Eji2.js';
2
+
3
+ interface BillingPlan {
4
+ id: string;
5
+ name: string;
6
+ price_cents: number;
7
+ token_allocation: number;
8
+ features: string;
9
+ sort_order?: number;
10
+ is_active?: boolean;
11
+ }
12
+ interface Subscription {
13
+ plan?: {
14
+ id: string;
15
+ name: string;
16
+ priceCents: number;
17
+ tokenAllocation: number;
18
+ };
19
+ planId?: string;
20
+ planName?: string;
21
+ status: string;
22
+ tokensUsed?: number;
23
+ tokensRemaining?: number;
24
+ usagePercent?: number;
25
+ periodStart?: number;
26
+ periodEnd?: number;
27
+ cancelAtPeriodEnd?: boolean;
28
+ stripeCustomerId?: string;
29
+ }
30
+ interface UsageSummary {
31
+ ownerId: string;
32
+ planAllocation: number;
33
+ planId: string | null;
34
+ planName: string;
35
+ inferenceUsed: number;
36
+ ledgerSpent: number;
37
+ totalUsed: number;
38
+ remaining: number;
39
+ percent: number;
40
+ exceeded: boolean;
41
+ breakdown: {
42
+ subscription: number;
43
+ prepaidCredit: number;
44
+ tokenGrants: number;
45
+ inference: number;
46
+ skillRegistrations: number;
47
+ skillRegistrationCount: number;
48
+ };
49
+ }
50
+ interface PrepaidCheckoutResult {
51
+ url: string;
52
+ sessionId?: string;
53
+ }
54
+ interface PrepaidVerifyResult {
55
+ alreadyCredited: boolean;
56
+ tokensCredited?: number;
57
+ amountCents?: number;
58
+ paymentRef?: string;
59
+ }
60
+ interface BillingRecord {
61
+ id: string;
62
+ recorded_at?: number;
63
+ date?: string;
64
+ reason?: string;
65
+ description?: string;
66
+ amount: number;
67
+ direction?: string;
68
+ source?: string;
69
+ status?: string;
70
+ payment_ref?: string;
71
+ }
72
+
73
+ /** Decode JWT payload without verification (server-side helper) */
74
+ declare function decodeJWTPayload(jwt: string): Record<string, any> | null;
75
+ /** Sign a JWT with HMAC-SHA256 */
76
+ declare function signJWT(payload: Record<string, any>, secret: string): string;
77
+ /** Verify a JWT signature with HMAC-SHA256 (constant-time comparison) */
78
+ declare function verifyJWTSignature(jwt: string, secret: string): boolean;
79
+ /** Verify JWT and return payload if valid (checks signature + expiry) */
80
+ declare function verifyJWT(jwt: string, secret: string): Record<string, any> | null;
81
+ /**
82
+ * Check if JWT needs refresh and return a new one if so.
83
+ * Returns null if no refresh needed or JWT is invalid.
84
+ */
85
+ declare function maybeRefreshJWT(jwt: string, secret: string, expirySeconds?: number, refreshWindowSeconds?: number): string | null;
86
+ /** Generate a cryptographically secure random token */
87
+ declare function generateToken(bytes?: number): string;
88
+ /** Options controlling how the real client IP is extracted from a Request.
89
+ *
90
+ * The previous implementation trusted `X-Forwarded-For[0]` unconditionally,
91
+ * which lets any direct caller pin arbitrary values and bypass per-IP rate
92
+ * limits. The correct read depends on how many reverse proxies sit between
93
+ * the app and the real client: with N trusted hops, the real client IP is
94
+ * at position `(length - N)` in the XFF list, because each trusted hop
95
+ * appends its peer IP and the attacker-controlled prefix is pushed left. */
96
+ interface IPExtractorConfig {
97
+ /** Number of trusted reverse-proxy hops between this app and the real client.
98
+ * - `0`: do NOT trust X-Forwarded-For (app is directly internet-exposed).
99
+ * - `1` (default): one trusted proxy (e.g. single LB, Vercel edge, Nginx).
100
+ * - `N`: N trusted hops; the real client IP is `XFF[length - N]`.
101
+ * If XFF has fewer entries than expected, the chain is misconfigured or
102
+ * spoofed and the extractor falls through to the next source. */
103
+ trustedProxyCount?: number;
104
+ /** Trust the `X-Real-IP` header. Only enable if your proxy sets it AND
105
+ * strips any inbound `X-Real-IP` from clients. Default: `false`. */
106
+ trustRealIpHeader?: boolean;
107
+ /** Override entirely — return the real client IP from a request however
108
+ * you know best (e.g. a platform-specific header like `cf-connecting-ip`
109
+ * or `x-vercel-forwarded-for`). If this returns a non-empty string it
110
+ * wins; if it returns null/empty the other strategies run. */
111
+ customExtractor?: (request: Request) => string | null | undefined;
112
+ }
113
+ /** Extract the real client IP address from a request.
114
+ *
115
+ * Default behavior (`trustedProxyCount: 1`) is safe for the common case of
116
+ * one trusted reverse proxy. Consumers with no proxy must pass `0`, and
117
+ * consumers behind multiple proxies must pass the exact hop count. */
118
+ declare function extractIP(request: Request, config?: IPExtractorConfig): string;
119
+
120
+ /** Client-side configuration for UserUtilsProvider */
121
+ interface UserUtilsConfig {
122
+ /** Base URL for API calls (empty string = same origin) */
123
+ apiBaseUrl: string;
124
+ /** StackNet stack identifier */
125
+ stackId?: string;
126
+ /** Direct StackNet URL for client-side API calls (e.g. challenges) */
127
+ stacknetUrl?: string;
128
+ /** Theme preference */
129
+ theme?: 'light' | 'dark' | 'system';
130
+ }
131
+ /** Callbacks for auth and billing events */
132
+ interface UserUtilsCallbacks {
133
+ onAuthSuccess?: (session: PublicSession) => void;
134
+ onAuthError?: (error: Error) => void;
135
+ onLogout?: () => void;
136
+ onSubscriptionChange?: (subscription: Subscription) => void;
137
+ }
138
+ /** Server-side configuration for handler factories */
139
+ interface ServerConfig {
140
+ /** HMAC-SHA256 secret for signing JWTs */
141
+ authSecret: string;
142
+ /** StackNet backend URL (always https://stacknet.magma-rpc.com) */
143
+ stacknetUrl: string;
144
+ /** StackNet stack identifier */
145
+ stackId: string;
146
+ /** JWT secret for re-signing to StackNet (defaults to authSecret) */
147
+ stacknetJwtSecret?: string;
148
+ /** Cookie domain for subdomain sharing (e.g. '.geoff.ai') */
149
+ cookieDomain?: string;
150
+ /** Use Secure flag on cookies (default: true) */
151
+ secureCookies?: boolean;
152
+ /** Session max age in seconds (default: 604800 = 7 days) */
153
+ sessionMaxAge?: number;
154
+ /** JWT expiry in seconds (default: 900 = 15 minutes) */
155
+ jwtExpiry?: number;
156
+ /**
157
+ * Google OAuth client ID (single). Used by createGoogleOneTapHandler to
158
+ * validate the `aud` claim on incoming Google ID tokens. Required for
159
+ * Google One Tap — without it, an ID token issued to any other Google
160
+ * application could be replayed against this endpoint.
161
+ */
162
+ googleClientId?: string;
163
+ /**
164
+ * Google OAuth client IDs (multiple). Use when the stack accepts tokens
165
+ * from more than one Google client (e.g. web + native).
166
+ */
167
+ googleClientIds?: string[];
168
+ /**
169
+ * How the handlers should derive the real client IP for rate-limit keys.
170
+ * Defaults to `{ trustedProxyCount: 1 }`. Set `trustedProxyCount: 0` if
171
+ * the app is exposed directly (no proxy) — otherwise any caller can pin
172
+ * their X-Forwarded-For and bypass rate limiting.
173
+ */
174
+ ipConfig?: IPExtractorConfig;
175
+ }
176
+
177
+ export { type BillingPlan as B, type IPExtractorConfig as I, type PrepaidCheckoutResult as P, type Subscription as S, type UsageSummary as U, type BillingRecord as a, type PrepaidVerifyResult as b, type UserUtilsCallbacks as c, type UserUtilsConfig as d, type ServerConfig as e, decodeJWTPayload as f, extractIP as g, generateToken as h, verifyJWTSignature as i, maybeRefreshJWT as m, signJWT as s, verifyJWT as v };
@@ -1,6 +1,6 @@
1
- import { a as AuthClientConfig, A as AuthClient } from '../types-CAoB_5kk.cjs';
2
- export { b as AuthTransport } from '../types-CAoB_5kk.cjs';
3
- import '../auth-DR2aYcor.cjs';
1
+ import { a as AuthClientConfig, A as AuthClient } from '../types-B_Vj6cr4.cjs';
2
+ export { b as AuthTransport } from '../types-B_Vj6cr4.cjs';
3
+ import '../auth-c1d7Eji2.cjs';
4
4
 
5
5
  /**
6
6
  * Platform-agnostic auth client factory.
@@ -1,6 +1,6 @@
1
- import { a as AuthClientConfig, A as AuthClient } from '../types-Dghy_8Wh.js';
2
- export { b as AuthTransport } from '../types-Dghy_8Wh.js';
3
- import '../auth-DR2aYcor.js';
1
+ import { a as AuthClientConfig, A as AuthClient } from '../types-Cu0do-w-.js';
2
+ export { b as AuthTransport } from '../types-Cu0do-w-.js';
3
+ import '../auth-c1d7Eji2.js';
4
4
 
5
5
  /**
6
6
  * Platform-agnostic auth client factory.
@@ -1,2 +1,2 @@
1
- 'use strict';var react=require('react');function le(){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 r=e.slice(18);return JSON.parse(atob(r.replace(/-/g,"+").replace(/_/g,"/")))}catch{return null}}function U(e="__csrf"){if(typeof document>"u")return null;let r=document.cookie.split(";").map(i=>i.trim()).find(i=>i.startsWith(`${e}=`));return r?r.slice(e.length+1):null}function M(){let[e,r]=react.useState(null),[i,l]=react.useState(true),d=react.useCallback(()=>{let s=le();s&&s.expiresAt>Date.now()?r({userId:s.userId,address:s.address,chain:s.chain,expiresAt:s.expiresAt,planId:s.planId,authMethod:s.authMethod}):r(null),l(false);},[]);react.useEffect(()=>{d();},[d]);let f=react.useCallback(async(s="")=>{try{let a=await fetch(`${s}/api/auth/session`);if(a.ok){let o=await a.json();if(o.session)return r(o.session),o.session}return r(null),null}catch{return null}},[]),p=!!e&&e.expiresAt>Date.now();return {session:e,loading:i,isAuthenticated:p,refresh:f,readSession:d}}function K(e="__csrf",r="x-csrf-token"){let[i,l]=react.useState(null);react.useEffect(()=>{l(U(e));},[e]);let d=i?{[r]:i}:{};return {token:i,headers:d}}function V(){let[e,r]=react.useState({connected:false,address:null,chain:null,provider:null}),[i,l]=react.useState(null),d=react.useCallback(async(a="phantom")=>{l(null);try{let o=typeof window<"u"?window:null,u=a==="phantom"?o?.phantom?.solana||o?.solana:o?.solflare;if(!u)return l(`${a} wallet not found`),null;let h=(await u.connect()).publicKey.toString();return r({connected:!0,address:h,chain:"solana",provider:a}),h}catch(o){return l(o.message||"Failed to connect wallet"),null}},[]),f=react.useCallback(async()=>{l(null);try{let o=(typeof window<"u"?window:null)?.ethereum;if(!o)return l("MetaMask not found"),null;let u=o;o.providers?.length&&(u=o.providers.find(t=>t.isMetaMask)||o);let h=(await u.request({method:"eth_requestAccounts"}))[0];return h?(r({connected:!0,address:h,chain:"ethereum",provider:"metamask"}),h):(l("No account selected"),null)}catch(a){return l(a.message||"Failed to connect wallet"),null}},[]),p=react.useCallback(async(a,o)=>{l(null);let u=o?.chain||e.chain,c=o?.provider||e.provider,h=o?.address||e.address;try{if(u==="solana"){let t=typeof window<"u"?window:null,g=c==="solflare"?t?.solflare:t?.phantom?.solana||t?.solana;if(!g)throw new Error("Wallet not available");let n=new TextEncoder().encode(a),m=await g.signMessage(n,"utf8"),y=new Uint8Array(m.signature||m),w="";for(let A=0;A<y.byteLength;A++)w+=String.fromCharCode(y[A]);return btoa(w)}if(u==="ethereum"){let g=(typeof window<"u"?window:null)?.ethereum;if(g?.providers?.length&&(g=g.providers.find(m=>m.isMetaMask)||g),!g)throw new Error("MetaMask not available");return await g.request({method:"personal_sign",params:[a,h]})}throw new Error("No wallet connected")}catch(t){return l(t.message||"Signing failed"),null}},[e]),s=react.useCallback(()=>{r({connected:false,address:null,chain:null,provider:null}),l(null);},[]);return {wallet:e,error:i,connectSolana:d,connectEVM:f,signMessage:p,disconnect:s}}var Pe="https://stacknet.magma-rpc.com/auth/bridge",J="stacknet-auth-bridge";function q(e){let r=e?.bridgeUrl||Pe,i=e?.disabled||false,l=react.useRef(null),[d,f]=react.useState({ready:false,known:false,identity:null,identityCount:0,resolvedStackId:null}),p=react.useRef([]),s=react.useRef(false),a=react.useCallback(t=>{let g={...t,protocol:J};s.current&&l.current?.contentWindow?l.current.contentWindow.postMessage(g,new URL(r).origin):p.current.push(g);},[r]);react.useEffect(()=>{if(i)return;let t=n=>{if(!(!n.data||n.data.protocol!==J)){try{if(n.origin!==new URL(r).origin)return}catch{return}switch(n.data.type){case "bridge:ready":s.current=true,f(m=>({...m,ready:true}));for(let m of p.current)l.current?.contentWindow?.postMessage(m,n.origin);p.current=[],l.current?.contentWindow?.postMessage({protocol:J,type:"auth:check"},n.origin),l.current?.contentWindow?.postMessage({protocol:J,type:"auth:resolve-stack"},n.origin);break;case "auth:status":f(m=>({...m,known:n.data.known,identity:n.data.identity,identityCount:n.data.identityCount||0}));break;case "auth:resolved-stack":f(m=>({...m,resolvedStackId:n.data.stackId||null}));break;}}};window.addEventListener("message",t);let g=document.createElement("iframe");return g.src=r,g.style.display="none",g.setAttribute("aria-hidden","true"),g.setAttribute("tabindex","-1"),g.setAttribute("sandbox","allow-scripts allow-same-origin"),document.body.appendChild(g),l.current=g,()=>{window.removeEventListener("message",t),g.parentNode&&g.parentNode.removeChild(g),l.current=null,s.current=false;}},[r,i]);let o=react.useCallback(t=>{a({type:"auth:connected",...t});},[a]),u=react.useCallback(t=>{a({type:"auth:disconnected",...t});},[a]),c=react.useCallback(()=>{a({type:"auth:clear"}),f({ready:d.ready,known:false,identity:null,identityCount:0,resolvedStackId:null});},[a,d.ready]),h=react.useCallback(()=>{a({type:"auth:check"});},[a]);return {...d,reportConnected:o,reportDisconnected:u,clearAll:c,refresh:h}}async function fe(e,r,i,l){let d=e.apiVersion||"v2",f=`${e.baseUrl}/api/${d}${i}`;try{let p=await fetch(f,{method:r,headers:{"Content-Type":"application/json"},body:l?JSON.stringify(l):void 0}),s=await p.json();return p.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(p){return {success:false,error:{code:"NETWORK_ERROR",message:p instanceof Error?p.message:"Network error"}}}}function pe(e){return {getNetworkStatus:()=>fe(e,"GET","/network/status"),getWeb3Challenge:(r,i)=>fe(e,"POST",`/stacks/${encodeURIComponent(e.stackId)}/auth/web3/challenge`,{chain:r,address:i})}}function Ae(e={apiBaseUrl:""}){let{wallet:r,connectSolana:i,connectEVM:l,signMessage:d,disconnect:f}=V(),{session:p,isAuthenticated:s,refresh:a,readSession:o}=M(),{headers:u}=K(),c=q({disabled:typeof window>"u"}),[h,t]=react.useState(false),[g,n]=react.useState(null),[m,y]=react.useState(false),w=e.apiBaseUrl||"",A=e.stacknetUrl||"https://stacknet.magma-rpc.com",C=e.stackId||c.resolvedStackId||"",S=pe({baseUrl:A,stackId:C}),E=react.useCallback(async(k,P,T,x)=>{t(true),n(null);try{let v=P;if(!v){let D=await T();if(!D)return t(!1),!1;v=D;}let O=await S.getWeb3Challenge(k,v);if(!O.success||!O.data)return n("Failed to get challenge"),t(!1),!1;let R=await d(O.data.message,{chain:k,provider:x,address:v});if(!R)return t(!1),!1;let G={chain:k,message:O.data.message,signature:R,stackId:C};k==="solana"&&(G.publicKey=v);let F=await fetch(`${w}/api/auth/callback`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify(G)});if(!F.ok){let D=await F.json().catch(()=>({}));return n(D.error||"Authentication failed"),t(!1),!1}return c.reportConnected({address:v,chain:k,method:x||(k==="solana"?"phantom":"metamask"),stackId:C}),o(),t(!1),!0}catch(v){return n(v.message||"Authentication failed"),t(false),false}},[w,S,d,o,c,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),n(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 T=await P.json().catch(()=>({}));return n(T.error||"Invalid code"),t(!1),!1}return o(),t(!1),!0}catch(P){return n(P.message||"OTP verification failed"),t(false),false}},[w,o]),I=react.useCallback(async(k,P)=>{t(true),n(null);try{let T=P||`${window.location.origin}/api/auth/oauth/callback`,x=new URLSearchParams({provider:k,redirectUri:T,stackId:C}),v=await fetch(`${w}/api/auth/oauth/start?${x}`,{credentials:"include"});if(!v.ok){let R=await v.json().catch(()=>({}));return n(R.error||"Failed to start OAuth flow"),t(!1),!1}let O=await v.json();if(O.redirect_url){let R;try{R=new URL(O.redirect_url);}catch{return n("Invalid OAuth redirect URL"),t(!1),!1}let G=[/(^|\.)accounts\.google\.com$/,/(^|\.)discord\.com$/,/(^|\.)github\.com$/,/(^|\.)x\.com$/,/(^|\.)twitter\.com$/,/(^|\.)apple\.com$/];return R.protocol!=="https:"||!G.some(F=>F.test(R.hostname))?(n(`Refusing to redirect to non-OAuth host: ${R.hostname}`),t(!1),!1):(typeof sessionStorage<"u"&&(sessionStorage.setItem("oauth_state",O.state||""),sessionStorage.setItem("oauth_provider",k)),window.location.href=R.toString(),!0)}return n("No redirect URL returned"),t(!1),!1}catch(T){return n(T.message||"OAuth flow failed"),t(false),false}},[w,C]),N=react.useCallback(async(k,P,T)=>{t(true),n(null);try{if(typeof sessionStorage<"u"){let v=sessionStorage.getItem("oauth_state"),O=sessionStorage.getItem("oauth_provider");if(sessionStorage.removeItem("oauth_state"),sessionStorage.removeItem("oauth_provider"),!v||v!==T)return n("OAuth state mismatch \u2014 refusing to complete login"),t(!1),!1;if(O&&O!==k)return n("OAuth provider mismatch \u2014 refusing to complete login"),t(!1),!1}let x=await fetch(`${w}/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(!x.ok){let v=await x.json().catch(()=>({}));return n(v.error||"OAuth authentication failed"),t(!1),!1}return o(),t(!1),!0}catch(x){return n(x.message||"OAuth callback failed"),t(false),false}},[w,C,o]),j=react.useCallback(async()=>{r.address&&r.chain&&c.reportDisconnected({address:r.address,chain:r.chain,stackId:C});try{await fetch(`${w}/api/auth/logout`,{method:"POST",headers:u,credentials:"include"});}catch{}f(),o();},[w,u,f,o,r,c,C]);return react.useEffect(()=>{if(!e.autoConnect||m||s||!c.ready||!c.known||!c.identity)return;y(true);let{chain:k,method:P}=c.identity;k==="solana"&&(P==="phantom"||P==="solflare")?$(P):k==="ethereum"&&W();},[e.autoConnect,m,s,c,$,W]),{session:p,isAuthenticated:s,wallet:r,loading:h,error:g,authenticateSolana:$,authenticateEVM:W,authenticateOTP:b,authenticateOAuth:I,authenticateOAuthCallback:N,logout:j,refresh:()=>a(w),stackId:C,bridge:{ready:c.ready,known:c.known,identity:c.identity,identityCount:c.identityCount,resolvedStackId:c.resolvedStackId}}}function Y(e,r="https://stacknet.magma-rpc.com"){let[i,l]=react.useState(null),[d,f]=react.useState(false),[p,s]=react.useState(null),a=react.useCallback(async u=>{f(true),s(null);try{let c=await fetch(`${r}/api/v2/stacks/${u}`);if(!c.ok)return s("Stack not found"),f(!1),null;let h=await c.json(),t=h.data?.stack||h.stack||h,g={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(n=>({provider:n.provider,clientId:n.clientId,enabled:n.enabled!==!1}))};return l(g),f(!1),g}catch(c){return s(c.message),f(false),null}},[r]);react.useEffect(()=>{e&&a(e);},[e,a]);let o=i?Te(i):[];return {config:i,loading:d,error:p,identityProviders:o,fetchConfig:a}}function Te(e){let r=[];if(e.features?.web3Auth!==false&&(e.allowedChains.includes("solana")&&(r.push({type:"wallet",id:"phantom",name:"Phantom",chain:"solana"}),r.push({type:"wallet",id:"solflare",name:"Solflare",chain:"solana"})),(e.allowedChains.includes("ethereum")||e.allowedChains.includes("polygon")||e.allowedChains.includes("base"))&&r.push({type:"wallet",id:"metamask",name:"MetaMask",chain:"ethereum"})),e.features?.apiKeyAuth!==false&&r.push({type:"otp",id:"otp",name:"Access Code"}),e.features?.oauthAuth&&e.oauthProviders)for(let i of e.oauthProviders)i.enabled&&r.push({type:"oauth",id:i.provider,name:i.provider});return r}function Ue(e=""){let[r,i]=react.useState([]),[l,d]=react.useState(true),[f,p]=react.useState(null),s=react.useCallback(async()=>{try{let a=await fetch(`${e}/api/billing/plans`);if(a.ok){let o=await a.json();i(o.plans||o||[]);}}catch(a){p(a.message);}finally{d(false);}},[e]);return react.useEffect(()=>{s();},[s]),{plans:r,loading:l,error:f,refresh:s}}function _e(e=""){let[r,i]=react.useState(null),[l,d]=react.useState(true),[f,p]=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){p(u.message);}finally{d(false);}},[e]);react.useEffect(()=>{s();},[s]);let a=react.useCallback(async u=>{let c=U(),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]),o=react.useCallback(async()=>{let u=U();return (await fetch(`${e}/api/billing/cancel`,{method:"POST",headers:u?{"x-csrf-token":u}:{}})).ok?(await s(),true):false},[e,s]);return {subscription:r,loading:l,error:f,refresh:s,subscribe:a,cancel:o}}function Ne(e=""){let[r,i]=react.useState(null),[l,d]=react.useState(true),[f,p]=react.useState(null),s=react.useCallback(async()=>{try{let a=await fetch(`${e}/api/billing/usage`);if(a.ok){let o=await a.json();i(o);}}catch(a){p(a.message);}finally{d(false);}},[e]);return react.useEffect(()=>{s();},[s]),{usage:r,loading:l,error:f,refresh:s}}function Me(e=""){let[r,i]=react.useState(false),[l,d]=react.useState(null),f=react.useCallback(async s=>{i(true),d(null);try{let a=U(),o=await fetch(`${e}/api/billing/prepaid`,{method:"POST",headers:{"Content-Type":"application/json",...a?{"x-csrf-token":a}:{}},body:JSON.stringify({amountCents:s})}),u=await o.json();return o.ok?u.url||null:(d(u.error||"Purchase failed"),null)}catch(a){return d(a.message),null}finally{i(false);}},[e]),p=react.useCallback(async s=>{i(true),d(null);try{let a=U(),o=await fetch(`${e}/api/billing/verify-prepaid`,{method:"POST",headers:{"Content-Type":"application/json",...a?{"x-csrf-token":a}:{}},body:JSON.stringify({sessionId:s})}),u=await o.json();return o.ok?u:(d(u.error||"Verification failed"),null)}catch(a){return d(a.message),null}finally{i(false);}},[e]);return {purchase:f,verifySession:p,loading:r,error:l}}function Ge(e="",r){let[i,l]=react.useState([]),[d,f]=react.useState(true),[p,s]=react.useState(null),a=r?.limit||50,o=r?.offset||0,u=react.useCallback(async()=>{try{let c=await fetch(`${e}/api/billing/history?limit=${a}&offset=${o}`);if(c.ok){let h=await c.json();l(h.records||h.history||(Array.isArray(h)?h:[]));}}catch(c){s(c.message);}finally{f(false);}},[e,a,o]);return react.useEffect(()=>{u();},[u]),{records:i,loading:d,error:p,refresh:u}}function De(){if(typeof document>"u")return null;let e=document.cookie.split(";").map(r=>r.trim()).find(r=>r.startsWith("__csrf="));return e?e.slice(7):null}function Be(e,r){let[i,l]=react.useState(null),[d,f]=react.useState(true),[p,s]=react.useState(false),[a,o]=react.useState(null),u=r?.apiBaseUrl??"",c=r?.scope===void 0||r?.scope==="global"?"global":`stack:${r.scope.stackId}`,h=react.useCallback(n=>{let m=encodeURIComponent(n);if(c==="global")return `${u}/api/user/profile/${m}`;let y=c.slice(6);return `${u}/api/v2/stacks/${encodeURIComponent(y)}/members/${m}/profile`},[u,c]),t=react.useCallback(async()=>{if(!e){l(null),f(false);return}f(true),o(null);try{let n=await fetch(h(e));if(n.ok){let m=await n.json(),y=m.profile||m.data?.profile||m;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(n.status===404)l({mid:e,username:""});else throw new Error(`${n.status}`)}catch(n){o(n instanceof Error?n.message:"Failed to load profile");}finally{f(false);}},[e,h]);react.useEffect(()=>{t();},[t]);let g=react.useCallback(async n=>{if(!e)return false;s(true),o(null);try{let m=De(),y={};n.username!==void 0&&(y.username=n.username),n.avatarUrl!==void 0&&(y.avatar_url=n.avatarUrl),n.bio!==void 0&&(y.bio=n.bio),n.paymentAddress!==void 0&&(y.payment_address=n.paymentAddress);let w=await fetch(h(e),{method:"PUT",headers:{"Content-Type":"application/json",...m?{"x-csrf-token":m}:{}},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:n.username??S?.username??"",avatarUrl:n.avatarUrl??S?.avatarUrl,bio:n.bio??S?.bio,paymentAddress:n.paymentAddress??C.payment_address??C.paymentAddress??S?.paymentAddress,createdAt:S?.createdAt,updatedAt:C.updated_at||C.updatedAt||Date.now()})),!0}catch(m){return o(m instanceof Error?m.message:"Update failed"),false}finally{s(false);}},[e,h]);return {profile:i,loading:d,saving:p,error:a,updateProfile:g,refresh:t}}var ye="google-identity-services",Je="https://accounts.google.com/gsi/client";function He({stackId:e,stacknetUrl:r="https://stacknet.magma-rpc.com",apiBaseUrl:i="",autoPrompt:l=true,cancelOnTapOutside:d=true,onSuccess:f,onError:p,disabled:s=false}){let{config:a}=Y(e,r),{isAuthenticated:o,loading:u,readSession:c}=M(),[h,t]=react.useState(false),[g,n]=react.useState(null),[m,y]=react.useState(false),w=react.useRef(false),A=react.useRef(false),S=a?.oauthProviders?.find(b=>b.provider==="google"&&b.enabled&&b.clientId)?.clientId||null;react.useEffect(()=>{if(s||!S||typeof window>"u")return;if(document.getElementById(ye)){y(true);return}let b=document.createElement("script");b.id=ye,b.src=Je,b.async=true,b.defer=true,b.onload=()=>y(true),b.onerror=()=>{n("Failed to load Google sign-in"),p?.("Failed to load Google Identity Services script");},document.head.appendChild(b);},[s,S,p]);let E=react.useCallback(async b=>{t(true),n(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";n(j),p?.(j),t(!1);return}c(),t(!1),f?.();}catch(I){let N=I.message||"Google sign-in failed";n(N),p?.(N),t(false);}},[i,e,c,f,p]);react.useEffect(()=>{if(s){console.debug("[GoogleOneTap] Disabled");return}if(!m){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(o){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?.());})));},[s,m,S,u,o,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:m&&!!S,loading:h,error:g,prompt:$,renderButton:W,clientId:S}}
2
- exports.useAuthBridge=q;exports.useBillingHistory=Ge;exports.useCSRFToken=K;exports.useGoogleOneTap=He;exports.usePlans=Ue;exports.usePrepaidCheckout=Me;exports.useProfile=Be;exports.useSession=M;exports.useStackAuth=Ae;exports.useStackConfig=Y;exports.useSubscription=_e;exports.useUsage=Ne;exports.useWeb3Wallet=V;
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,5 +1,5 @@
1
- import { P as PublicSession, W as Web3Chain } from '../auth-DR2aYcor.cjs';
2
- import { d as UserUtilsConfig, B as BillingPlan, S as Subscription, U as UsageSummary, b as PrepaidVerifyResult, a as BillingRecord } from '../config-Bjh8PEhY.cjs';
1
+ import { P as PublicSession, W as Web3Chain } from '../auth-c1d7Eji2.cjs';
2
+ import { d as UserUtilsConfig, B as BillingPlan, S as Subscription, U as UsageSummary, b as PrepaidVerifyResult, a as BillingRecord } from '../config-CLzVWDrU.cjs';
3
3
  import { ProfileScope, UserProfile, ProfileUpdateInput } from '../types/index.cjs';
4
4
 
5
5
  /**
@@ -296,4 +296,53 @@ declare function useGoogleOneTap({ stackId, stacknetUrl, apiBaseUrl, autoPrompt,
296
296
  clientId: string | null;
297
297
  };
298
298
 
299
- export { type BridgeIdentity, type GoogleOneTapConfig, type StackPublicConfig, type WalletState, useAuthBridge, useBillingHistory, useCSRFToken, useGoogleOneTap, usePlans, usePrepaidCheckout, useProfile, useSession, useStackAuth, useStackConfig, useSubscription, useUsage, useWeb3Wallet };
299
+ /**
300
+ * useJoinCode — fetch (and lazy-mint) the signed-in user's affiliate code.
301
+ *
302
+ * The code is the user's stable invite identifier — backed by stacknet's
303
+ * `user_join_codes` table. A new user who signs up via `?ref=<code>` in
304
+ * the URL has `POST /user/join` called once during auth (see
305
+ * `use-stack-auth.ts`), binding them permanently to the referring user
306
+ * and awarding the referrer 20 social points (decayed per the schedule).
307
+ *
308
+ * This hook is a thin client over `GET /social/join-code` (read-only) and
309
+ * `POST /social/join-code` (fetch-or-mint). On first call we POST so the
310
+ * code exists; subsequent renders read from cached state. `copyShareLink`
311
+ * composes a full invite URL using the configured `stacknetUrl` host.
312
+ */
313
+ interface UseJoinCodeResult {
314
+ /** The code string (e.g. 'aB3xK9pQ'), or null while loading / when signed out. */
315
+ code: string | null;
316
+ /** Full URL including `?ref=<code>`, suitable for sharing. */
317
+ shareUrl: string | null;
318
+ /** True while the initial fetch/mint is in flight. */
319
+ loading: boolean;
320
+ /** Last error from the fetch/mint call, if any. */
321
+ error: string | null;
322
+ /** Manually re-fetch (usually unnecessary — the code is stable). */
323
+ refresh: () => Promise<void>;
324
+ /**
325
+ * Copy the share URL to the clipboard. Returns true on success. A no-op
326
+ * (returning false) when the browser doesn't support the clipboard API
327
+ * — the caller can fall back to showing the URL inline.
328
+ */
329
+ copyShareLink: () => Promise<boolean>;
330
+ }
331
+ interface UseJoinCodeConfig {
332
+ /**
333
+ * Base URL of the consumer app. The share link is `${shareBaseUrl}/?ref=<code>`
334
+ * so the landing page hands the code to stacknet via `POST /user/join`
335
+ * on the new user's first auth. Defaults to `window.location.origin`
336
+ * (the page the hook runs on), which is the right choice for most apps.
337
+ */
338
+ shareBaseUrl?: string;
339
+ /**
340
+ * If false, the hook only reads the existing code (GET) and won't mint
341
+ * one if absent. Useful when you want to show the invite section only
342
+ * after the user has explicitly asked for their code. Default: true.
343
+ */
344
+ autoMint?: boolean;
345
+ }
346
+ declare function useJoinCode(config?: UseJoinCodeConfig): UseJoinCodeResult;
347
+
348
+ 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 };