@stacknet/userutils 0.4.1 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
- export { A as APIResponse, B as BillingPlan, a as BillingRecord, M as MPCNode, N as NetworkStatus, P as PrepaidCheckoutResult, b as PrepaidVerifyResult, c as PublicSession, S as Session, d as Subscription, U as UsageSummary, e as UserUtilsCallbacks, f as UserUtilsConfig, W as Web3Chain } from '../config-BrziGArs.cjs';
1
+ export { A as APIResponse, M as MPCNode, N as NetworkStatus, P as PublicSession, S as Session, W as Web3Chain } from '../auth-DR2aYcor.cjs';
2
+ export { B as BillingPlan, a as BillingRecord, P as PrepaidCheckoutResult, b as PrepaidVerifyResult, S as Subscription, U as UsageSummary, c as UserUtilsCallbacks, d as UserUtilsConfig } from '../config-Bjh8PEhY.cjs';
2
3
 
3
4
  /** User profile — global (network-wide) or stack-scoped */
4
5
  interface UserProfile {
@@ -6,6 +7,12 @@ interface UserProfile {
6
7
  username: string;
7
8
  avatarUrl?: string;
8
9
  bio?: string;
10
+ /**
11
+ * Stack-scoped payout address (e.g. Solana wallet for Paper / USDC payouts).
12
+ * Only meaningful on stack-scoped profiles. The backend stores this as
13
+ * `payment_address` (snake_case).
14
+ */
15
+ paymentAddress?: string;
9
16
  createdAt?: number;
10
17
  updatedAt?: number;
11
18
  }
@@ -14,6 +21,12 @@ interface ProfileUpdateInput {
14
21
  username?: string;
15
22
  avatarUrl?: string;
16
23
  bio?: string;
24
+ /**
25
+ * Stack-scoped payout address. Pass through `useProfile().updateProfile`
26
+ * with a stack-scoped scope to set the payment_address on the user's
27
+ * profile for that stack.
28
+ */
29
+ paymentAddress?: string;
17
30
  }
18
31
  /** Scope for profile operations */
19
32
  type ProfileScope = 'global' | {
@@ -1,4 +1,5 @@
1
- export { A as APIResponse, B as BillingPlan, a as BillingRecord, M as MPCNode, N as NetworkStatus, P as PrepaidCheckoutResult, b as PrepaidVerifyResult, c as PublicSession, S as Session, d as Subscription, U as UsageSummary, e as UserUtilsCallbacks, f as UserUtilsConfig, W as Web3Chain } from '../config-BrziGArs.js';
1
+ export { A as APIResponse, M as MPCNode, N as NetworkStatus, P as PublicSession, S as Session, W as Web3Chain } from '../auth-DR2aYcor.js';
2
+ export { B as BillingPlan, a as BillingRecord, P as PrepaidCheckoutResult, b as PrepaidVerifyResult, S as Subscription, U as UsageSummary, c as UserUtilsCallbacks, d as UserUtilsConfig } from '../config-_ZjAzNkJ.js';
2
3
 
3
4
  /** User profile — global (network-wide) or stack-scoped */
4
5
  interface UserProfile {
@@ -6,6 +7,12 @@ interface UserProfile {
6
7
  username: string;
7
8
  avatarUrl?: string;
8
9
  bio?: string;
10
+ /**
11
+ * Stack-scoped payout address (e.g. Solana wallet for Paper / USDC payouts).
12
+ * Only meaningful on stack-scoped profiles. The backend stores this as
13
+ * `payment_address` (snake_case).
14
+ */
15
+ paymentAddress?: string;
9
16
  createdAt?: number;
10
17
  updatedAt?: number;
11
18
  }
@@ -14,6 +21,12 @@ interface ProfileUpdateInput {
14
21
  username?: string;
15
22
  avatarUrl?: string;
16
23
  bio?: string;
24
+ /**
25
+ * Stack-scoped payout address. Pass through `useProfile().updateProfile`
26
+ * with a stack-scoped scope to set the payment_address on the user's
27
+ * profile for that stack.
28
+ */
29
+ paymentAddress?: string;
17
30
  }
18
31
  /** Scope for profile operations */
19
32
  type ProfileScope = 'global' | {
@@ -0,0 +1,60 @@
1
+ import { P as PublicSession, W as Web3Chain } from './auth-DR2aYcor.cjs';
2
+
3
+ interface AuthTransport {
4
+ /** Store credentials after successful login */
5
+ storeCredentials(token: string, session: PublicSession): Promise<void>;
6
+ /** Retrieve the stored auth token (null if not authenticated) */
7
+ getToken(): Promise<string | null>;
8
+ /** Get headers to attach to authenticated API requests */
9
+ getHeaders(): Promise<Record<string, string>>;
10
+ /** Read the stored session without a network call */
11
+ getStoredSession(): Promise<PublicSession | null>;
12
+ /** Clear all stored auth data */
13
+ clear(): Promise<void>;
14
+ }
15
+ interface AuthClientConfig {
16
+ /** Base URL for app API routes (e.g. http://localhost:3000) */
17
+ apiBaseUrl: string;
18
+ /** Direct StackNet URL for challenges (defaults to https://stacknet.magma-rpc.com) */
19
+ stacknetUrl?: string;
20
+ /** Stack identifier */
21
+ stackId?: string;
22
+ /** Auth transport (web cookies or native secure storage) */
23
+ transport: AuthTransport;
24
+ /** Optional fallback token for dev (e.g. service key) */
25
+ serviceKey?: string;
26
+ /** Callbacks */
27
+ onAuthSuccess?: (session: PublicSession) => void;
28
+ onAuthError?: (error: Error) => void;
29
+ onLogout?: () => void;
30
+ }
31
+ interface AuthClient {
32
+ /** Email + password login */
33
+ login(email: string, password: string): Promise<PublicSession>;
34
+ /** Web3 wallet login (challenge must be obtained first) */
35
+ loginWeb3(params: {
36
+ chain: Web3Chain;
37
+ address: string;
38
+ message: string;
39
+ signature: string;
40
+ }): Promise<PublicSession>;
41
+ /** OTP code login */
42
+ loginOTP(code: string): Promise<PublicSession>;
43
+ /** Get current session (from storage, then validate with server) */
44
+ getSession(): Promise<PublicSession | null>;
45
+ /** Check if token is valid with the server */
46
+ checkSession(): Promise<boolean>;
47
+ /** Get a web3 challenge for wallet signing */
48
+ getChallenge(chain: Web3Chain, address: string): Promise<{
49
+ message: string;
50
+ nonce: string;
51
+ }>;
52
+ /** Sign out and clear stored credentials */
53
+ logout(): Promise<void>;
54
+ /** Get auth headers for API requests */
55
+ getHeaders(): Promise<Record<string, string>>;
56
+ /** Get stored token directly */
57
+ getToken(): Promise<string | null>;
58
+ }
59
+
60
+ export type { AuthClient as A, AuthClientConfig as a, AuthTransport as b };
@@ -0,0 +1,60 @@
1
+ import { P as PublicSession, W as Web3Chain } from './auth-DR2aYcor.js';
2
+
3
+ interface AuthTransport {
4
+ /** Store credentials after successful login */
5
+ storeCredentials(token: string, session: PublicSession): Promise<void>;
6
+ /** Retrieve the stored auth token (null if not authenticated) */
7
+ getToken(): Promise<string | null>;
8
+ /** Get headers to attach to authenticated API requests */
9
+ getHeaders(): Promise<Record<string, string>>;
10
+ /** Read the stored session without a network call */
11
+ getStoredSession(): Promise<PublicSession | null>;
12
+ /** Clear all stored auth data */
13
+ clear(): Promise<void>;
14
+ }
15
+ interface AuthClientConfig {
16
+ /** Base URL for app API routes (e.g. http://localhost:3000) */
17
+ apiBaseUrl: string;
18
+ /** Direct StackNet URL for challenges (defaults to https://stacknet.magma-rpc.com) */
19
+ stacknetUrl?: string;
20
+ /** Stack identifier */
21
+ stackId?: string;
22
+ /** Auth transport (web cookies or native secure storage) */
23
+ transport: AuthTransport;
24
+ /** Optional fallback token for dev (e.g. service key) */
25
+ serviceKey?: string;
26
+ /** Callbacks */
27
+ onAuthSuccess?: (session: PublicSession) => void;
28
+ onAuthError?: (error: Error) => void;
29
+ onLogout?: () => void;
30
+ }
31
+ interface AuthClient {
32
+ /** Email + password login */
33
+ login(email: string, password: string): Promise<PublicSession>;
34
+ /** Web3 wallet login (challenge must be obtained first) */
35
+ loginWeb3(params: {
36
+ chain: Web3Chain;
37
+ address: string;
38
+ message: string;
39
+ signature: string;
40
+ }): Promise<PublicSession>;
41
+ /** OTP code login */
42
+ loginOTP(code: string): Promise<PublicSession>;
43
+ /** Get current session (from storage, then validate with server) */
44
+ getSession(): Promise<PublicSession | null>;
45
+ /** Check if token is valid with the server */
46
+ checkSession(): Promise<boolean>;
47
+ /** Get a web3 challenge for wallet signing */
48
+ getChallenge(chain: Web3Chain, address: string): Promise<{
49
+ message: string;
50
+ nonce: string;
51
+ }>;
52
+ /** Sign out and clear stored credentials */
53
+ logout(): Promise<void>;
54
+ /** Get auth headers for API requests */
55
+ getHeaders(): Promise<Record<string, string>>;
56
+ /** Get stored token directly */
57
+ getToken(): Promise<string | null>;
58
+ }
59
+
60
+ export type { AuthClient as A, AuthClientConfig as a, AuthTransport as b };
@@ -1,2 +1,2 @@
1
- 'use strict';var clsx=require('clsx'),tailwindMerge=require('tailwind-merge');function i(...t){return tailwindMerge.twMerge(clsx.clsx(t))}function s(t){return t>=1e12?`${(t/1e12).toFixed(t%1e12===0?0:1)}T`:t>=1e9?`${(t/1e9).toFixed(t%1e9===0?0:1)}B`:t>=1e6?`${(t/1e6).toFixed(t%1e6===0?0:1)}M`:t>=1e3?`${(t/1e3).toFixed(0)}K`:t.toLocaleString()}function a(t,r){if(!t)return "/";if(t.startsWith("/")&&!t.startsWith("//"))return t;try{let e=new URL(t,r);return e.origin!==r?"/":e.pathname+e.search+e.hash}catch{return "/"}}function u(t){try{let r=t.split(".");if(r.length!==3)return null;let e=atob(r[1].replace(/-/g,"+").replace(/_/g,"/"));return JSON.parse(e)}catch{return null}}function c(){if(typeof document>"u")return null;try{let t=document.cookie.split(";").map(e=>e.trim()).find(e=>e.startsWith("stackauth_session="));if(!t)return null;let r=t.slice(18);return JSON.parse(atob(r.replace(/-/g,"+").replace(/_/g,"/")))}catch{return null}}function l(t="__csrf"){if(typeof document>"u")return null;let r=document.cookie.split(";").map(e=>e.trim()).find(e=>e.startsWith(`${t}=`));return r?r.slice(t.length+1):null}
2
- exports.cn=i;exports.decodeJwtPayloadClient=u;exports.formatTokens=s;exports.readCSRFCookie=l;exports.readSessionCookie=c;exports.validateRedirectUrl=a;
1
+ 'use strict';var clsx=require('clsx'),tailwindMerge=require('tailwind-merge');function s(...t){return tailwindMerge.twMerge(clsx.clsx(t))}function a(t){return t>=1e12?`${(t/1e12).toFixed(t%1e12===0?0:1)}T`:t>=1e9?`${(t/1e9).toFixed(t%1e9===0?0:1)}B`:t>=1e6?`${(t/1e6).toFixed(t%1e6===0?0:1)}M`:t>=1e3?`${(t/1e3).toFixed(0)}K`:t.toLocaleString()}function u(t,e){if(!t)return "/";if(t.startsWith("/")&&!t.startsWith("//"))return t;try{let r=new URL(t,e);return r.origin!==e?"/":r.pathname+r.search+r.hash}catch{return "/"}}function c(t,e="#"){if(!t||typeof t!="string")return e;let r=t.trim();if(r===""||r==="#")return e;if(r.startsWith("/")||r.startsWith("./")||r.startsWith("../"))return r;try{let n=new URL(r);if(n.protocol==="http:"||n.protocol==="https:")return n.toString()}catch{}return e}function l(t){try{let e=t.split(".");if(e.length!==3)return null;let r=atob(e[1].replace(/-/g,"+").replace(/_/g,"/"));return JSON.parse(r)}catch{return null}}function d(){if(typeof document>"u")return null;try{let t=document.cookie.split(";").map(r=>r.trim()).find(r=>r.startsWith("stackauth_session="));if(!t)return null;let e=t.slice(18);return JSON.parse(atob(e.replace(/-/g,"+").replace(/_/g,"/")))}catch{return null}}function f(t="__csrf"){if(typeof document>"u")return null;let e=document.cookie.split(";").map(r=>r.trim()).find(r=>r.startsWith(`${t}=`));return e?e.slice(t.length+1):null}
2
+ exports.cn=s;exports.decodeJwtPayloadClient=l;exports.formatTokens=a;exports.readCSRFCookie=f;exports.readSessionCookie=d;exports.safeUrl=c;exports.validateRedirectUrl=u;
@@ -11,6 +11,21 @@ declare function formatTokens(n: number): string;
11
11
  */
12
12
  declare function validateRedirectUrl(url: string, allowedOrigin: string): string;
13
13
 
14
+ /**
15
+ * Returns a safe href value for an anchor or image source.
16
+ *
17
+ * Allows: relative paths (starting with /, ./, ../), and absolute http(s) URLs.
18
+ * Blocks: javascript:, data:, file:, vbscript:, mailto:, etc.
19
+ *
20
+ * React only sanitizes `javascript:` in href attributes by default; this
21
+ * helper closes the gap for `data:` (which can carry HTML/JS) and any other
22
+ * non-network protocol that a misconfigured or malicious config could supply.
23
+ *
24
+ * Returns the supplied `fallback` (default '#') for any input that fails
25
+ * validation, so the markup never renders an unsafe URL.
26
+ */
27
+ declare function safeUrl(url: string | undefined | null, fallback?: string): string;
28
+
14
29
  /**
15
30
  * Client-side JWT payload decode — NO VERIFICATION.
16
31
  * For display purposes only. Never trust the result for auth decisions.
@@ -33,4 +48,4 @@ declare function readSessionCookie(): {
33
48
  */
34
49
  declare function readCSRFCookie(cookieName?: string): string | null;
35
50
 
36
- export { cn, decodeJwtPayloadClient, formatTokens, readCSRFCookie, readSessionCookie, validateRedirectUrl };
51
+ export { cn, decodeJwtPayloadClient, formatTokens, readCSRFCookie, readSessionCookie, safeUrl, validateRedirectUrl };
@@ -11,6 +11,21 @@ declare function formatTokens(n: number): string;
11
11
  */
12
12
  declare function validateRedirectUrl(url: string, allowedOrigin: string): string;
13
13
 
14
+ /**
15
+ * Returns a safe href value for an anchor or image source.
16
+ *
17
+ * Allows: relative paths (starting with /, ./, ../), and absolute http(s) URLs.
18
+ * Blocks: javascript:, data:, file:, vbscript:, mailto:, etc.
19
+ *
20
+ * React only sanitizes `javascript:` in href attributes by default; this
21
+ * helper closes the gap for `data:` (which can carry HTML/JS) and any other
22
+ * non-network protocol that a misconfigured or malicious config could supply.
23
+ *
24
+ * Returns the supplied `fallback` (default '#') for any input that fails
25
+ * validation, so the markup never renders an unsafe URL.
26
+ */
27
+ declare function safeUrl(url: string | undefined | null, fallback?: string): string;
28
+
14
29
  /**
15
30
  * Client-side JWT payload decode — NO VERIFICATION.
16
31
  * For display purposes only. Never trust the result for auth decisions.
@@ -33,4 +48,4 @@ declare function readSessionCookie(): {
33
48
  */
34
49
  declare function readCSRFCookie(cookieName?: string): string | null;
35
50
 
36
- export { cn, decodeJwtPayloadClient, formatTokens, readCSRFCookie, readSessionCookie, validateRedirectUrl };
51
+ export { cn, decodeJwtPayloadClient, formatTokens, readCSRFCookie, readSessionCookie, safeUrl, validateRedirectUrl };
@@ -1,2 +1,2 @@
1
- import {clsx}from'clsx';import {twMerge}from'tailwind-merge';function i(...t){return twMerge(clsx(t))}function s(t){return t>=1e12?`${(t/1e12).toFixed(t%1e12===0?0:1)}T`:t>=1e9?`${(t/1e9).toFixed(t%1e9===0?0:1)}B`:t>=1e6?`${(t/1e6).toFixed(t%1e6===0?0:1)}M`:t>=1e3?`${(t/1e3).toFixed(0)}K`:t.toLocaleString()}function a(t,r){if(!t)return "/";if(t.startsWith("/")&&!t.startsWith("//"))return t;try{let e=new URL(t,r);return e.origin!==r?"/":e.pathname+e.search+e.hash}catch{return "/"}}function u(t){try{let r=t.split(".");if(r.length!==3)return null;let e=atob(r[1].replace(/-/g,"+").replace(/_/g,"/"));return JSON.parse(e)}catch{return null}}function c(){if(typeof document>"u")return null;try{let t=document.cookie.split(";").map(e=>e.trim()).find(e=>e.startsWith("stackauth_session="));if(!t)return null;let r=t.slice(18);return JSON.parse(atob(r.replace(/-/g,"+").replace(/_/g,"/")))}catch{return null}}function l(t="__csrf"){if(typeof document>"u")return null;let r=document.cookie.split(";").map(e=>e.trim()).find(e=>e.startsWith(`${t}=`));return r?r.slice(t.length+1):null}
2
- export{i as cn,u as decodeJwtPayloadClient,s as formatTokens,l as readCSRFCookie,c as readSessionCookie,a as validateRedirectUrl};
1
+ import {clsx}from'clsx';import {twMerge}from'tailwind-merge';function s(...t){return twMerge(clsx(t))}function a(t){return t>=1e12?`${(t/1e12).toFixed(t%1e12===0?0:1)}T`:t>=1e9?`${(t/1e9).toFixed(t%1e9===0?0:1)}B`:t>=1e6?`${(t/1e6).toFixed(t%1e6===0?0:1)}M`:t>=1e3?`${(t/1e3).toFixed(0)}K`:t.toLocaleString()}function u(t,e){if(!t)return "/";if(t.startsWith("/")&&!t.startsWith("//"))return t;try{let r=new URL(t,e);return r.origin!==e?"/":r.pathname+r.search+r.hash}catch{return "/"}}function c(t,e="#"){if(!t||typeof t!="string")return e;let r=t.trim();if(r===""||r==="#")return e;if(r.startsWith("/")||r.startsWith("./")||r.startsWith("../"))return r;try{let n=new URL(r);if(n.protocol==="http:"||n.protocol==="https:")return n.toString()}catch{}return e}function l(t){try{let e=t.split(".");if(e.length!==3)return null;let r=atob(e[1].replace(/-/g,"+").replace(/_/g,"/"));return JSON.parse(r)}catch{return null}}function d(){if(typeof document>"u")return null;try{let t=document.cookie.split(";").map(r=>r.trim()).find(r=>r.startsWith("stackauth_session="));if(!t)return null;let e=t.slice(18);return JSON.parse(atob(e.replace(/-/g,"+").replace(/_/g,"/")))}catch{return null}}function f(t="__csrf"){if(typeof document>"u")return null;let e=document.cookie.split(";").map(r=>r.trim()).find(r=>r.startsWith(`${t}=`));return e?e.slice(t.length+1):null}
2
+ export{s as cn,l as decodeJwtPayloadClient,a as formatTokens,f as readCSRFCookie,d as readSessionCookie,c as safeUrl,u as validateRedirectUrl};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stacknet/userutils",
3
- "version": "0.4.1",
3
+ "version": "0.5.3",
4
4
  "description": "Reusable auth, billing, and security utilities for StackNet stacks and applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -36,6 +36,16 @@
36
36
  "types": "./dist/server/index.d.ts",
37
37
  "import": "./dist/server/index.js",
38
38
  "require": "./dist/server/index.cjs"
39
+ },
40
+ "./core": {
41
+ "types": "./dist/core/index.d.ts",
42
+ "import": "./dist/core/index.js",
43
+ "require": "./dist/core/index.cjs"
44
+ },
45
+ "./adapters": {
46
+ "types": "./dist/adapters/index.d.ts",
47
+ "import": "./dist/adapters/index.js",
48
+ "require": "./dist/adapters/index.cjs"
39
49
  }
40
50
  },
41
51
  "files": [