@stacknet/keyutils 0.5.0 → 0.7.1

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 +1 @@
1
- 'use strict';
1
+ 'use strict';var e=["stripe","solana","eth","btc"],t={stripe:{method:"stripe",label:"Credit Card",kind:"card"},solana:{method:"solana",label:"Solana",kind:"crypto"},eth:{method:"eth",label:"Ethereum",kind:"crypto"},btc:{method:"btc",label:"Bitcoin",kind:"crypto"}};exports.KNOWN_PAYMENT_METHODS=e;exports.PAYMENT_METHOD_META=t;
@@ -42,7 +42,19 @@ interface MintResult {
42
42
  transactionId?: string;
43
43
  error?: string;
44
44
  }
45
- type PaymentMethod = 'solana' | 'stripe';
45
+ type PaymentMethod = 'solana' | 'stripe' | 'eth' | 'btc';
46
+ /** Every method this UI package knows how to render. Used to filter the
47
+ * backend-supplied list so unknown/disabled rails never render. */
48
+ declare const KNOWN_PAYMENT_METHODS: readonly PaymentMethod[];
49
+ /** Presentational metadata for a payment method (label + lucide icon name).
50
+ * Purely for display; carries no chain config. */
51
+ interface PaymentMethodMeta {
52
+ method: PaymentMethod;
53
+ label: string;
54
+ /** Whether the rail is a credit-card (fiat) rail vs a crypto wallet rail. */
55
+ kind: 'card' | 'crypto';
56
+ }
57
+ declare const PAYMENT_METHOD_META: Record<PaymentMethod, PaymentMethodMeta>;
46
58
  interface KeyListing {
47
59
  id: string;
48
60
  keyId: string;
@@ -94,10 +106,19 @@ type EligibleKey = NodeKeyInfo & {
94
106
  };
95
107
  type PayoutAccountMethod = 'stripe' | 'solana';
96
108
  /**
97
- * The user's saved payout account. One method per user, by design — switching
98
- * methods replaces the previous record. Stripe Connect uses Express accounts
99
- * driven by the stack's platform credentials; Solana uses a wallet address
100
- * the user proved control of by signing a server-issued nonce.
109
+ * The user's saved payout config in the context of one stack.
110
+ *
111
+ * Stripe Connect is per-(user, stack) each stack runs its own platform
112
+ * under its own secret, so a given user has independent connected accounts
113
+ * across stacks. The `stripe` field reflects the connection for the SCOPE
114
+ * stack only; query other stacks via `useUserStripeStacks()` or by passing
115
+ * a different `stackId` to `usePayoutAccount`.
116
+ *
117
+ * The Solana wallet is global (one verified address services every stack).
118
+ *
119
+ * `method` records the user's preferred rail. The active filing flow
120
+ * verifies the chosen rail is configured for the key's originating stack
121
+ * and falls back to a clear error if not.
101
122
  */
102
123
  interface PayoutAccount {
103
124
  /** Which method is currently configured. `null` when nothing is set up. */
@@ -123,6 +144,19 @@ interface PayoutAccount {
123
144
  verifiedAt: string;
124
145
  };
125
146
  }
147
+ /**
148
+ * One stack the user has a Stripe Connect account on. Returned by
149
+ * `useUserStripeStacks()` for the multi-stack settings view.
150
+ */
151
+ interface StripeStackPayout {
152
+ stackId: string;
153
+ accountIdMasked: string;
154
+ chargesEnabled: boolean;
155
+ payoutsEnabled: boolean;
156
+ detailsSubmitted: boolean;
157
+ pendingOnboardingUrl?: string | null;
158
+ updatedAt: string;
159
+ }
126
160
  interface CryptoNonceResponse {
127
161
  nonce: string;
128
162
  /** Canonical message the wallet must sign — includes the nonce. */
@@ -224,4 +258,4 @@ interface KeyUtilsCallbacks {
224
258
  onFilingError?: (error: Error) => void;
225
259
  }
226
260
 
227
- export type { CryptoNonceResponse, DirectTransferResult, EligibleKey, FilingDetail, FilingHistoryEntry, FilingResult, FilingStatus, FilingTrackerState, FilingTrackerStatus, FilingTrackerStep, KeyListing, KeyUtilsCallbacks, KeyUtilsConfig, KeyWidgetTab, LedgerEntry, ListingResult, MintResult, NodeKeyInfo, PaymentMethod, PayoutAccount, PayoutAccountMethod, PayoutMethod, Platform, PlatformDownload, PricingInfo, StackPayoutCapabilities, StripeConnectStartResponse };
261
+ export { type CryptoNonceResponse, type DirectTransferResult, type EligibleKey, type FilingDetail, type FilingHistoryEntry, type FilingResult, type FilingStatus, type FilingTrackerState, type FilingTrackerStatus, type FilingTrackerStep, KNOWN_PAYMENT_METHODS, type KeyListing, type KeyUtilsCallbacks, type KeyUtilsConfig, type KeyWidgetTab, type LedgerEntry, type ListingResult, type MintResult, type NodeKeyInfo, PAYMENT_METHOD_META, type PaymentMethod, type PaymentMethodMeta, type PayoutAccount, type PayoutAccountMethod, type PayoutMethod, type Platform, type PlatformDownload, type PricingInfo, type StackPayoutCapabilities, type StripeConnectStartResponse, type StripeStackPayout };
@@ -42,7 +42,19 @@ interface MintResult {
42
42
  transactionId?: string;
43
43
  error?: string;
44
44
  }
45
- type PaymentMethod = 'solana' | 'stripe';
45
+ type PaymentMethod = 'solana' | 'stripe' | 'eth' | 'btc';
46
+ /** Every method this UI package knows how to render. Used to filter the
47
+ * backend-supplied list so unknown/disabled rails never render. */
48
+ declare const KNOWN_PAYMENT_METHODS: readonly PaymentMethod[];
49
+ /** Presentational metadata for a payment method (label + lucide icon name).
50
+ * Purely for display; carries no chain config. */
51
+ interface PaymentMethodMeta {
52
+ method: PaymentMethod;
53
+ label: string;
54
+ /** Whether the rail is a credit-card (fiat) rail vs a crypto wallet rail. */
55
+ kind: 'card' | 'crypto';
56
+ }
57
+ declare const PAYMENT_METHOD_META: Record<PaymentMethod, PaymentMethodMeta>;
46
58
  interface KeyListing {
47
59
  id: string;
48
60
  keyId: string;
@@ -94,10 +106,19 @@ type EligibleKey = NodeKeyInfo & {
94
106
  };
95
107
  type PayoutAccountMethod = 'stripe' | 'solana';
96
108
  /**
97
- * The user's saved payout account. One method per user, by design — switching
98
- * methods replaces the previous record. Stripe Connect uses Express accounts
99
- * driven by the stack's platform credentials; Solana uses a wallet address
100
- * the user proved control of by signing a server-issued nonce.
109
+ * The user's saved payout config in the context of one stack.
110
+ *
111
+ * Stripe Connect is per-(user, stack) each stack runs its own platform
112
+ * under its own secret, so a given user has independent connected accounts
113
+ * across stacks. The `stripe` field reflects the connection for the SCOPE
114
+ * stack only; query other stacks via `useUserStripeStacks()` or by passing
115
+ * a different `stackId` to `usePayoutAccount`.
116
+ *
117
+ * The Solana wallet is global (one verified address services every stack).
118
+ *
119
+ * `method` records the user's preferred rail. The active filing flow
120
+ * verifies the chosen rail is configured for the key's originating stack
121
+ * and falls back to a clear error if not.
101
122
  */
102
123
  interface PayoutAccount {
103
124
  /** Which method is currently configured. `null` when nothing is set up. */
@@ -123,6 +144,19 @@ interface PayoutAccount {
123
144
  verifiedAt: string;
124
145
  };
125
146
  }
147
+ /**
148
+ * One stack the user has a Stripe Connect account on. Returned by
149
+ * `useUserStripeStacks()` for the multi-stack settings view.
150
+ */
151
+ interface StripeStackPayout {
152
+ stackId: string;
153
+ accountIdMasked: string;
154
+ chargesEnabled: boolean;
155
+ payoutsEnabled: boolean;
156
+ detailsSubmitted: boolean;
157
+ pendingOnboardingUrl?: string | null;
158
+ updatedAt: string;
159
+ }
126
160
  interface CryptoNonceResponse {
127
161
  nonce: string;
128
162
  /** Canonical message the wallet must sign — includes the nonce. */
@@ -224,4 +258,4 @@ interface KeyUtilsCallbacks {
224
258
  onFilingError?: (error: Error) => void;
225
259
  }
226
260
 
227
- export type { CryptoNonceResponse, DirectTransferResult, EligibleKey, FilingDetail, FilingHistoryEntry, FilingResult, FilingStatus, FilingTrackerState, FilingTrackerStatus, FilingTrackerStep, KeyListing, KeyUtilsCallbacks, KeyUtilsConfig, KeyWidgetTab, LedgerEntry, ListingResult, MintResult, NodeKeyInfo, PaymentMethod, PayoutAccount, PayoutAccountMethod, PayoutMethod, Platform, PlatformDownload, PricingInfo, StackPayoutCapabilities, StripeConnectStartResponse };
261
+ export { type CryptoNonceResponse, type DirectTransferResult, type EligibleKey, type FilingDetail, type FilingHistoryEntry, type FilingResult, type FilingStatus, type FilingTrackerState, type FilingTrackerStatus, type FilingTrackerStep, KNOWN_PAYMENT_METHODS, type KeyListing, type KeyUtilsCallbacks, type KeyUtilsConfig, type KeyWidgetTab, type LedgerEntry, type ListingResult, type MintResult, type NodeKeyInfo, PAYMENT_METHOD_META, type PaymentMethod, type PaymentMethodMeta, type PayoutAccount, type PayoutAccountMethod, type PayoutMethod, type Platform, type PlatformDownload, type PricingInfo, type StackPayoutCapabilities, type StripeConnectStartResponse, type StripeStackPayout };
@@ -0,0 +1 @@
1
+ var e=["stripe","solana","eth","btc"],t={stripe:{method:"stripe",label:"Credit Card",kind:"card"},solana:{method:"solana",label:"Solana",kind:"crypto"},eth:{method:"eth",label:"Ethereum",kind:"crypto"},btc:{method:"btc",label:"Bitcoin",kind:"crypto"}};export{e as KNOWN_PAYMENT_METHODS,t as PAYMENT_METHOD_META};
@@ -1 +1 @@
1
- 'use strict';var clsx=require('clsx'),tailwindMerge=require('tailwind-merge');var d=false;function _(){if(typeof document>"u")return {};let e=document.cookie.match(/(?:^|;\s*)__csrf=([^;]*)/);if(!e)return d||(d=true,console.warn("[keyutils] __csrf cookie not found; mutations will be sent without a CSRF token")),{};let n=e[1];try{n=decodeURIComponent(n);}catch{}return {"x-csrf-token":n}}var b=500;function g(e,n){if(typeof e!="string"||e.length===0)return n;let t=e.replace(/[\u0000-\u001F\u007F]/g," ");return t.length>500?t.slice(0,500)+"\u2026":t}async function R(e,n){try{let t=await e.json();if(t&&typeof t=="object"&&"error"in t)return g(t.error,n)}catch{}return n}function F(e,n="#"){if(!e||typeof e!="string")return n;let t=e.trim();if(t===""||t==="#")return n;if(t.startsWith("/")||t.startsWith("./")||t.startsWith("../"))return t;try{let o=new URL(t);if(o.protocol==="http:"||o.protocol==="https:")return o.toString()}catch{}return n}var c={sent:"sent",routed:"routed",confirmed:"confirmed",finalized:"finalized",filed:"filed"},u={sent:{pending:"sent",progressing:"sending",done:"sent"},routed:{pending:"routed",progressing:"routing",done:"routed"},confirmed:{pending:"confirmed",progressing:"confirming",done:"confirmed"},finalized:{pending:"finalized",progressing:"finalizing",done:"finalized"},filed:{pending:"filed",progressing:"filing",done:"filed"}},k=["sent","routed","confirmed","finalized","filed"];function T(e){let n=new Map;(e??[]).forEach(r=>n.set(r.id,r));let t=k.map(r=>{let i=n.get(r);return i?{...i,label:i.label||c[r]}:{id:r,label:c[r],status:"pending",timestampMs:null,detail:null}}),o=t.reduce((r,i,a)=>i.status==="done"||i.status==="failed"?a:r,-1);if(o>0)for(let r=0;r<o;r++)t[r].status!=="failed"&&(t[r]={...t[r],status:"done"});if(!t.some(r=>r.status==="failed")){let r=t.findIndex(i=>i.status!=="done");if(r>=0){let i=t[r];i.status!=="current"&&(t[r]={...i,status:"current"});}}return t}function p(e){return e.find(n=>n.status==="current")??null}function m(e){return e.length>0&&e.every(n=>n.status==="done")}function x(e){return e.some(n=>n.status==="failed")}function E(e,n){if(!n)return {text:"loading",playing:true,stepId:"sent"};if(x(e))return {text:"failed",playing:false,stepId:"filed"};if(m(e))return {text:u.filed.done,playing:false,stepId:"filed"};let o=p(e)?.id??"sent";return {text:u[o].progressing,playing:true,stepId:o}}function v(...e){return tailwindMerge.twMerge(clsx.clsx(e))}function P(e){return e==null?"0":e>=1e9?`${(e/1e9).toFixed(1)}B`:e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:e.toLocaleString()}function z(e,n){return n<=0?0:Math.min(100,Math.round(e/n*100))}function U(e){return e>=70?"bg-green-500":e>=30?"bg-yellow-500":"bg-red-500"}function K(e,n={}){let t=n.withSymbol!==false,o=Math.max(0,Math.min(6,n.maxDecimals??2));if(e==null||e==="")return t?"$0.00":"0.00";let s;try{if(typeof e=="bigint")s=e;else if(typeof e=="number")s=BigInt(Math.trunc(e));else {let l=String(e).trim().replace(/^\+/,"");if(/^-?\d+$/.test(l))s=BigInt(l);else {let f=Number(l);if(!Number.isFinite(f))return t?"$0.00":"0.00";s=BigInt(Math.trunc(f));}}}catch{return t?"$0.00":"0.00"}let r=s<0n,i=r?-s:s,a=i/1000000n,S=(i%1000000n).toString().padStart(6,"0").slice(0,o),h=a.toLocaleString("en-US"),y=o>0?`.${S}`:"";return `${r?"-":""}${t?"$":""}${h}${y}`}function O(e){if(e==null||e==="")return "";let n=typeof e=="string"&&/^\d+$/.test(e)?Number(e):e,t=new Date(n);return isNaN(t.getTime())?"":t.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"})}function H(){if(typeof navigator>"u")return "mac";let e=navigator.userAgent.toLowerCase();return /iphone|ipad|ipod/.test(e)?"ios":/android/.test(e)?"android":/win/.test(e)?"windows":/linux/.test(e)?"linux":"mac"}exports.MAX_ERROR_LEN=b;exports.TRACKER_LABELS=c;exports.TRACKER_VERBS=u;exports.calculateBalancePercentage=z;exports.clampErrorMessage=g;exports.cn=v;exports.csrfHeaders=_;exports.currentState=p;exports.deriveTrackerStates=T;exports.detectPlatform=H;exports.formatDate=O;exports.formatPaperUsd=K;exports.formatTokens=P;exports.getBalanceColor=U;exports.hasFailed=x;exports.isAllDone=m;exports.pickHeadline=E;exports.readErrorMessage=R;exports.safeUrl=F;
1
+ 'use strict';var clsx=require('clsx'),tailwindMerge=require('tailwind-merge');var f=false;function S(){if(typeof document>"u")return {};let e=document.cookie.match(/(?:^|;\s*)__csrf=([^;]*)/);if(!e)return f||(f=true,console.warn("[keyutils] __csrf cookie not found; mutations will be sent without a CSRF token")),{};let n=e[1];try{n=decodeURIComponent(n);}catch{}return {"x-csrf-token":n}}var T=500;function m(e,n){if(typeof e!="string"||e.length===0)return n;let t=e.replace(/[\u0000-\u001F\u007F]/g," ");return t.length>500?t.slice(0,500)+"\u2026":t}async function P(e,n){try{let t=await e.json();if(t&&typeof t=="object"&&"error"in t)return m(t.error,n)}catch{}return n}function R(e,n="#"){if(!e||typeof e!="string")return n;let t=e.trim();if(t===""||t==="#")return n;if(t.startsWith("/")||t.startsWith("./")||t.startsWith("../"))return t;try{let i=new URL(t);if(i.protocol==="http:"||i.protocol==="https:")return i.toString()}catch{}return n}var d=["stripe","solana","eth","btc"],c={stripe:{method:"stripe",label:"Credit Card",kind:"card"},solana:{method:"solana",label:"Solana",kind:"crypto"},eth:{method:"eth",label:"Ethereum",kind:"crypto"},btc:{method:"btc",label:"Bitcoin",kind:"crypto"}};function F(e){let n=e&&e.length>0?e:["solana"],t=new Set;for(let i of n){if(typeof i!="string")continue;let s=i.trim().toLowerCase(),r=d.find(o=>o===s);r&&t.add(r);}return d.filter(i=>t.has(i))}function E(e){return c[e]?.label??e}function I(e){return c[e]?.kind==="crypto"}var u={sent:"sent",routed:"routed",confirmed:"confirmed",finalized:"finalized",filed:"filed"},g={sent:{pending:"sent",progressing:"sending",done:"sent"},routed:{pending:"routed",progressing:"routing",done:"routed"},confirmed:{pending:"confirmed",progressing:"confirming",done:"confirmed"},finalized:{pending:"finalized",progressing:"finalizing",done:"finalized"},filed:{pending:"filed",progressing:"filing",done:"filed"}},_=["sent","routed","confirmed","finalized","filed"];function A(e){let n=new Map;(e??[]).forEach(r=>n.set(r.id,r));let t=_.map(r=>{let o=n.get(r);return o?{...o,label:o.label||u[r]}:{id:r,label:u[r],status:"pending",timestampMs:null,detail:null}}),i=t.reduce((r,o,a)=>o.status==="done"||o.status==="failed"?a:r,-1);if(i>0)for(let r=0;r<i;r++)t[r].status!=="failed"&&(t[r]={...t[r],status:"done"});if(!t.some(r=>r.status==="failed")){let r=t.findIndex(o=>o.status!=="done");if(r>=0){let o=t[r];o.status!=="current"&&(t[r]={...o,status:"current"});}}return t}function y(e){return e.find(n=>n.status==="current")??null}function b(e){return e.length>0&&e.every(n=>n.status==="done")}function x(e){return e.some(n=>n.status==="failed")}function C(e,n){if(!n)return {text:"loading",playing:true,stepId:"sent"};if(x(e))return {text:"failed",playing:false,stepId:"filed"};if(b(e))return {text:g.filed.done,playing:false,stepId:"filed"};let i=y(e)?.id??"sent";return {text:g[i].progressing,playing:true,stepId:i}}function V(...e){return tailwindMerge.twMerge(clsx.clsx(e))}function X(e){return e==null?"0":e>=1e9?`${(e/1e9).toFixed(1)}B`:e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:e.toLocaleString()}function Y(e,n){return n<=0?0:Math.min(100,Math.round(e/n*100))}function j(e){return e>=70?"bg-green-500":e>=30?"bg-yellow-500":"bg-red-500"}function q(e,n={}){let t=n.withSymbol!==false,i=Math.max(0,Math.min(6,n.maxDecimals??2));if(e==null||e==="")return t?"$0.00":"0.00";let s;try{if(typeof e=="bigint")s=e;else if(typeof e=="number")s=BigInt(Math.trunc(e));else {let l=String(e).trim().replace(/^\+/,"");if(/^-?\d+$/.test(l))s=BigInt(l);else {let p=Number(l);if(!Number.isFinite(p))return t?"$0.00":"0.00";s=BigInt(Math.trunc(p));}}}catch{return t?"$0.00":"0.00"}let r=s<0n,o=r?-s:s,a=o/1000000n,h=(o%1000000n).toString().padStart(6,"0").slice(0,i),k=a.toLocaleString("en-US"),M=i>0?`.${h}`:"";return `${r?"-":""}${t?"$":""}${k}${M}`}function Q(e){if(e==null||e==="")return "";let n=typeof e=="string"&&/^\d+$/.test(e)?Number(e):e,t=new Date(n);return isNaN(t.getTime())?"":t.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"})}function G(){if(typeof navigator>"u")return "mac";let e=navigator.userAgent.toLowerCase();return /iphone|ipad|ipod/.test(e)?"ios":/android/.test(e)?"android":/win/.test(e)?"windows":/linux/.test(e)?"linux":"mac"}exports.MAX_ERROR_LEN=T;exports.TRACKER_LABELS=u;exports.TRACKER_VERBS=g;exports.calculateBalancePercentage=Y;exports.clampErrorMessage=m;exports.cn=V;exports.csrfHeaders=S;exports.currentState=y;exports.deriveTrackerStates=A;exports.detectPlatform=G;exports.formatDate=Q;exports.formatPaperUsd=q;exports.formatTokens=X;exports.getBalanceColor=j;exports.hasFailed=x;exports.isAllDone=b;exports.isCryptoPaymentMethod=I;exports.paymentMethodLabel=E;exports.pickHeadline=C;exports.readErrorMessage=P;exports.safeUrl=R;exports.supportedPaymentMethods=F;
@@ -1,5 +1,5 @@
1
1
  import { ClassValue } from 'clsx';
2
- import { FilingTrackerStep, FilingTrackerState, Platform } from '../types/index.cjs';
2
+ import { PaymentMethod, FilingTrackerStep, FilingTrackerState, Platform } from '../types/index.cjs';
3
3
 
4
4
  /**
5
5
  * Read the `__csrf` cookie and return it as the `x-csrf-token` header for
@@ -43,6 +43,34 @@ declare function readErrorMessage(res: Response, fallback: string): Promise<stri
43
43
  */
44
44
  declare function safeUrl(url: string | undefined | null, fallback?: string): string;
45
45
 
46
+ /**
47
+ * Normalize the backend-supplied list of enabled payment rails into the
48
+ * ordered, validated set of {@link PaymentMethod}s this UI should render.
49
+ *
50
+ * The backend (the rail layer, #241/#242/#243) is the source of truth for
51
+ * which rails are enabled. The widget MUST NOT hardcode the option list — it
52
+ * passes whatever the backend reports through here so the selector stays in
53
+ * sync as rails are turned on/off. This helper:
54
+ *
55
+ * - drops unknown / disabled methods (anything not in KNOWN_PAYMENT_METHODS),
56
+ * so a typo or a not-yet-shipped rail never renders a dead button;
57
+ * - de-duplicates;
58
+ * - applies a stable display order (card first, then crypto rails).
59
+ *
60
+ * This is display-only: no chain config, no derivation, no signing. Selecting
61
+ * a crypto rail drives the existing deposit-address / pending display, which
62
+ * the backend rail response populates.
63
+ *
64
+ * @param raw The methods the backend reports as enabled (e.g. from config or
65
+ * the pricing/rails endpoint). `undefined`/empty falls back to the
66
+ * historical default of Solana only.
67
+ */
68
+ declare function supportedPaymentMethods(raw?: readonly (string | null | undefined)[] | null): PaymentMethod[];
69
+ /** Human label for a payment method (e.g. 'eth' → 'Ethereum'). */
70
+ declare function paymentMethodLabel(method: PaymentMethod): string;
71
+ /** True when the rail is an on-chain crypto rail (vs a fiat card rail). */
72
+ declare function isCryptoPaymentMethod(method: PaymentMethod): boolean;
73
+
46
74
  declare const TRACKER_LABELS: Record<FilingTrackerStep, string>;
47
75
  /**
48
76
  * Verb tenses per state. `pending` is the neutral noun used when the row
@@ -92,4 +120,4 @@ declare function formatPaperUsd(raw: string | number | bigint | null | undefined
92
120
  declare function formatDate(dateString: string | number | null | undefined): string;
93
121
  declare function detectPlatform(): Platform;
94
122
 
95
- export { MAX_ERROR_LEN, TRACKER_LABELS, TRACKER_VERBS, calculateBalancePercentage, clampErrorMessage, cn, csrfHeaders, currentState, deriveTrackerStates, detectPlatform, formatDate, formatPaperUsd, formatTokens, getBalanceColor, hasFailed, isAllDone, pickHeadline, readErrorMessage, safeUrl };
123
+ export { MAX_ERROR_LEN, TRACKER_LABELS, TRACKER_VERBS, calculateBalancePercentage, clampErrorMessage, cn, csrfHeaders, currentState, deriveTrackerStates, detectPlatform, formatDate, formatPaperUsd, formatTokens, getBalanceColor, hasFailed, isAllDone, isCryptoPaymentMethod, paymentMethodLabel, pickHeadline, readErrorMessage, safeUrl, supportedPaymentMethods };
@@ -1,5 +1,5 @@
1
1
  import { ClassValue } from 'clsx';
2
- import { FilingTrackerStep, FilingTrackerState, Platform } from '../types/index.js';
2
+ import { PaymentMethod, FilingTrackerStep, FilingTrackerState, Platform } from '../types/index.js';
3
3
 
4
4
  /**
5
5
  * Read the `__csrf` cookie and return it as the `x-csrf-token` header for
@@ -43,6 +43,34 @@ declare function readErrorMessage(res: Response, fallback: string): Promise<stri
43
43
  */
44
44
  declare function safeUrl(url: string | undefined | null, fallback?: string): string;
45
45
 
46
+ /**
47
+ * Normalize the backend-supplied list of enabled payment rails into the
48
+ * ordered, validated set of {@link PaymentMethod}s this UI should render.
49
+ *
50
+ * The backend (the rail layer, #241/#242/#243) is the source of truth for
51
+ * which rails are enabled. The widget MUST NOT hardcode the option list — it
52
+ * passes whatever the backend reports through here so the selector stays in
53
+ * sync as rails are turned on/off. This helper:
54
+ *
55
+ * - drops unknown / disabled methods (anything not in KNOWN_PAYMENT_METHODS),
56
+ * so a typo or a not-yet-shipped rail never renders a dead button;
57
+ * - de-duplicates;
58
+ * - applies a stable display order (card first, then crypto rails).
59
+ *
60
+ * This is display-only: no chain config, no derivation, no signing. Selecting
61
+ * a crypto rail drives the existing deposit-address / pending display, which
62
+ * the backend rail response populates.
63
+ *
64
+ * @param raw The methods the backend reports as enabled (e.g. from config or
65
+ * the pricing/rails endpoint). `undefined`/empty falls back to the
66
+ * historical default of Solana only.
67
+ */
68
+ declare function supportedPaymentMethods(raw?: readonly (string | null | undefined)[] | null): PaymentMethod[];
69
+ /** Human label for a payment method (e.g. 'eth' → 'Ethereum'). */
70
+ declare function paymentMethodLabel(method: PaymentMethod): string;
71
+ /** True when the rail is an on-chain crypto rail (vs a fiat card rail). */
72
+ declare function isCryptoPaymentMethod(method: PaymentMethod): boolean;
73
+
46
74
  declare const TRACKER_LABELS: Record<FilingTrackerStep, string>;
47
75
  /**
48
76
  * Verb tenses per state. `pending` is the neutral noun used when the row
@@ -92,4 +120,4 @@ declare function formatPaperUsd(raw: string | number | bigint | null | undefined
92
120
  declare function formatDate(dateString: string | number | null | undefined): string;
93
121
  declare function detectPlatform(): Platform;
94
122
 
95
- export { MAX_ERROR_LEN, TRACKER_LABELS, TRACKER_VERBS, calculateBalancePercentage, clampErrorMessage, cn, csrfHeaders, currentState, deriveTrackerStates, detectPlatform, formatDate, formatPaperUsd, formatTokens, getBalanceColor, hasFailed, isAllDone, pickHeadline, readErrorMessage, safeUrl };
123
+ export { MAX_ERROR_LEN, TRACKER_LABELS, TRACKER_VERBS, calculateBalancePercentage, clampErrorMessage, cn, csrfHeaders, currentState, deriveTrackerStates, detectPlatform, formatDate, formatPaperUsd, formatTokens, getBalanceColor, hasFailed, isAllDone, isCryptoPaymentMethod, paymentMethodLabel, pickHeadline, readErrorMessage, safeUrl, supportedPaymentMethods };
@@ -1 +1 @@
1
- import {clsx}from'clsx';import {twMerge}from'tailwind-merge';var d=false;function _(){if(typeof document>"u")return {};let e=document.cookie.match(/(?:^|;\s*)__csrf=([^;]*)/);if(!e)return d||(d=true,console.warn("[keyutils] __csrf cookie not found; mutations will be sent without a CSRF token")),{};let n=e[1];try{n=decodeURIComponent(n);}catch{}return {"x-csrf-token":n}}var b=500;function g(e,n){if(typeof e!="string"||e.length===0)return n;let t=e.replace(/[\u0000-\u001F\u007F]/g," ");return t.length>500?t.slice(0,500)+"\u2026":t}async function R(e,n){try{let t=await e.json();if(t&&typeof t=="object"&&"error"in t)return g(t.error,n)}catch{}return n}function F(e,n="#"){if(!e||typeof e!="string")return n;let t=e.trim();if(t===""||t==="#")return n;if(t.startsWith("/")||t.startsWith("./")||t.startsWith("../"))return t;try{let o=new URL(t);if(o.protocol==="http:"||o.protocol==="https:")return o.toString()}catch{}return n}var c={sent:"sent",routed:"routed",confirmed:"confirmed",finalized:"finalized",filed:"filed"},u={sent:{pending:"sent",progressing:"sending",done:"sent"},routed:{pending:"routed",progressing:"routing",done:"routed"},confirmed:{pending:"confirmed",progressing:"confirming",done:"confirmed"},finalized:{pending:"finalized",progressing:"finalizing",done:"finalized"},filed:{pending:"filed",progressing:"filing",done:"filed"}},k=["sent","routed","confirmed","finalized","filed"];function T(e){let n=new Map;(e??[]).forEach(r=>n.set(r.id,r));let t=k.map(r=>{let i=n.get(r);return i?{...i,label:i.label||c[r]}:{id:r,label:c[r],status:"pending",timestampMs:null,detail:null}}),o=t.reduce((r,i,a)=>i.status==="done"||i.status==="failed"?a:r,-1);if(o>0)for(let r=0;r<o;r++)t[r].status!=="failed"&&(t[r]={...t[r],status:"done"});if(!t.some(r=>r.status==="failed")){let r=t.findIndex(i=>i.status!=="done");if(r>=0){let i=t[r];i.status!=="current"&&(t[r]={...i,status:"current"});}}return t}function p(e){return e.find(n=>n.status==="current")??null}function m(e){return e.length>0&&e.every(n=>n.status==="done")}function x(e){return e.some(n=>n.status==="failed")}function E(e,n){if(!n)return {text:"loading",playing:true,stepId:"sent"};if(x(e))return {text:"failed",playing:false,stepId:"filed"};if(m(e))return {text:u.filed.done,playing:false,stepId:"filed"};let o=p(e)?.id??"sent";return {text:u[o].progressing,playing:true,stepId:o}}function v(...e){return twMerge(clsx(e))}function P(e){return e==null?"0":e>=1e9?`${(e/1e9).toFixed(1)}B`:e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:e.toLocaleString()}function z(e,n){return n<=0?0:Math.min(100,Math.round(e/n*100))}function U(e){return e>=70?"bg-green-500":e>=30?"bg-yellow-500":"bg-red-500"}function K(e,n={}){let t=n.withSymbol!==false,o=Math.max(0,Math.min(6,n.maxDecimals??2));if(e==null||e==="")return t?"$0.00":"0.00";let s;try{if(typeof e=="bigint")s=e;else if(typeof e=="number")s=BigInt(Math.trunc(e));else {let l=String(e).trim().replace(/^\+/,"");if(/^-?\d+$/.test(l))s=BigInt(l);else {let f=Number(l);if(!Number.isFinite(f))return t?"$0.00":"0.00";s=BigInt(Math.trunc(f));}}}catch{return t?"$0.00":"0.00"}let r=s<0n,i=r?-s:s,a=i/1000000n,S=(i%1000000n).toString().padStart(6,"0").slice(0,o),h=a.toLocaleString("en-US"),y=o>0?`.${S}`:"";return `${r?"-":""}${t?"$":""}${h}${y}`}function O(e){if(e==null||e==="")return "";let n=typeof e=="string"&&/^\d+$/.test(e)?Number(e):e,t=new Date(n);return isNaN(t.getTime())?"":t.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"})}function H(){if(typeof navigator>"u")return "mac";let e=navigator.userAgent.toLowerCase();return /iphone|ipad|ipod/.test(e)?"ios":/android/.test(e)?"android":/win/.test(e)?"windows":/linux/.test(e)?"linux":"mac"}export{b as MAX_ERROR_LEN,c as TRACKER_LABELS,u as TRACKER_VERBS,z as calculateBalancePercentage,g as clampErrorMessage,v as cn,_ as csrfHeaders,p as currentState,T as deriveTrackerStates,H as detectPlatform,O as formatDate,K as formatPaperUsd,P as formatTokens,U as getBalanceColor,x as hasFailed,m as isAllDone,E as pickHeadline,R as readErrorMessage,F as safeUrl};
1
+ import {clsx}from'clsx';import {twMerge}from'tailwind-merge';var f=false;function S(){if(typeof document>"u")return {};let e=document.cookie.match(/(?:^|;\s*)__csrf=([^;]*)/);if(!e)return f||(f=true,console.warn("[keyutils] __csrf cookie not found; mutations will be sent without a CSRF token")),{};let n=e[1];try{n=decodeURIComponent(n);}catch{}return {"x-csrf-token":n}}var T=500;function m(e,n){if(typeof e!="string"||e.length===0)return n;let t=e.replace(/[\u0000-\u001F\u007F]/g," ");return t.length>500?t.slice(0,500)+"\u2026":t}async function P(e,n){try{let t=await e.json();if(t&&typeof t=="object"&&"error"in t)return m(t.error,n)}catch{}return n}function R(e,n="#"){if(!e||typeof e!="string")return n;let t=e.trim();if(t===""||t==="#")return n;if(t.startsWith("/")||t.startsWith("./")||t.startsWith("../"))return t;try{let i=new URL(t);if(i.protocol==="http:"||i.protocol==="https:")return i.toString()}catch{}return n}var d=["stripe","solana","eth","btc"],c={stripe:{method:"stripe",label:"Credit Card",kind:"card"},solana:{method:"solana",label:"Solana",kind:"crypto"},eth:{method:"eth",label:"Ethereum",kind:"crypto"},btc:{method:"btc",label:"Bitcoin",kind:"crypto"}};function F(e){let n=e&&e.length>0?e:["solana"],t=new Set;for(let i of n){if(typeof i!="string")continue;let s=i.trim().toLowerCase(),r=d.find(o=>o===s);r&&t.add(r);}return d.filter(i=>t.has(i))}function E(e){return c[e]?.label??e}function I(e){return c[e]?.kind==="crypto"}var u={sent:"sent",routed:"routed",confirmed:"confirmed",finalized:"finalized",filed:"filed"},g={sent:{pending:"sent",progressing:"sending",done:"sent"},routed:{pending:"routed",progressing:"routing",done:"routed"},confirmed:{pending:"confirmed",progressing:"confirming",done:"confirmed"},finalized:{pending:"finalized",progressing:"finalizing",done:"finalized"},filed:{pending:"filed",progressing:"filing",done:"filed"}},_=["sent","routed","confirmed","finalized","filed"];function A(e){let n=new Map;(e??[]).forEach(r=>n.set(r.id,r));let t=_.map(r=>{let o=n.get(r);return o?{...o,label:o.label||u[r]}:{id:r,label:u[r],status:"pending",timestampMs:null,detail:null}}),i=t.reduce((r,o,a)=>o.status==="done"||o.status==="failed"?a:r,-1);if(i>0)for(let r=0;r<i;r++)t[r].status!=="failed"&&(t[r]={...t[r],status:"done"});if(!t.some(r=>r.status==="failed")){let r=t.findIndex(o=>o.status!=="done");if(r>=0){let o=t[r];o.status!=="current"&&(t[r]={...o,status:"current"});}}return t}function y(e){return e.find(n=>n.status==="current")??null}function b(e){return e.length>0&&e.every(n=>n.status==="done")}function x(e){return e.some(n=>n.status==="failed")}function C(e,n){if(!n)return {text:"loading",playing:true,stepId:"sent"};if(x(e))return {text:"failed",playing:false,stepId:"filed"};if(b(e))return {text:g.filed.done,playing:false,stepId:"filed"};let i=y(e)?.id??"sent";return {text:g[i].progressing,playing:true,stepId:i}}function V(...e){return twMerge(clsx(e))}function X(e){return e==null?"0":e>=1e9?`${(e/1e9).toFixed(1)}B`:e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:e.toLocaleString()}function Y(e,n){return n<=0?0:Math.min(100,Math.round(e/n*100))}function j(e){return e>=70?"bg-green-500":e>=30?"bg-yellow-500":"bg-red-500"}function q(e,n={}){let t=n.withSymbol!==false,i=Math.max(0,Math.min(6,n.maxDecimals??2));if(e==null||e==="")return t?"$0.00":"0.00";let s;try{if(typeof e=="bigint")s=e;else if(typeof e=="number")s=BigInt(Math.trunc(e));else {let l=String(e).trim().replace(/^\+/,"");if(/^-?\d+$/.test(l))s=BigInt(l);else {let p=Number(l);if(!Number.isFinite(p))return t?"$0.00":"0.00";s=BigInt(Math.trunc(p));}}}catch{return t?"$0.00":"0.00"}let r=s<0n,o=r?-s:s,a=o/1000000n,h=(o%1000000n).toString().padStart(6,"0").slice(0,i),k=a.toLocaleString("en-US"),M=i>0?`.${h}`:"";return `${r?"-":""}${t?"$":""}${k}${M}`}function Q(e){if(e==null||e==="")return "";let n=typeof e=="string"&&/^\d+$/.test(e)?Number(e):e,t=new Date(n);return isNaN(t.getTime())?"":t.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"})}function G(){if(typeof navigator>"u")return "mac";let e=navigator.userAgent.toLowerCase();return /iphone|ipad|ipod/.test(e)?"ios":/android/.test(e)?"android":/win/.test(e)?"windows":/linux/.test(e)?"linux":"mac"}export{T as MAX_ERROR_LEN,u as TRACKER_LABELS,g as TRACKER_VERBS,Y as calculateBalancePercentage,m as clampErrorMessage,V as cn,S as csrfHeaders,y as currentState,A as deriveTrackerStates,G as detectPlatform,Q as formatDate,q as formatPaperUsd,X as formatTokens,j as getBalanceColor,x as hasFailed,b as isAllDone,I as isCryptoPaymentMethod,E as paymentMethodLabel,C as pickHeadline,P as readErrorMessage,R as safeUrl,F as supportedPaymentMethods};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stacknet/keyutils",
3
- "version": "0.5.0",
3
+ "version": "0.7.1",
4
4
  "description": "Reusable components for buying and managing StackNet node keys",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -42,16 +42,6 @@
42
42
  "dist",
43
43
  "README.md"
44
44
  ],
45
- "scripts": {
46
- "build": "tsup",
47
- "build:publish": "tsup --no-sourcemap --minify",
48
- "dev": "tsup --watch",
49
- "clean": "rm -rf dist",
50
- "typecheck": "tsc --noEmit",
51
- "prepublishOnly": "pnpm run clean && pnpm run typecheck && pnpm run build:publish",
52
- "release": "pnpm run prepublishOnly && npm publish --access public",
53
- "release:dry": "pnpm run prepublishOnly && npm pack --dry-run"
54
- },
55
45
  "dependencies": {
56
46
  "class-variance-authority": "^0.7.0",
57
47
  "clsx": "^2.1.1",
@@ -83,5 +73,16 @@
83
73
  "license": "MIT",
84
74
  "repository": {
85
75
  "type": "git"
76
+ },
77
+ "scripts": {
78
+ "build": "tsup",
79
+ "test": "bun test",
80
+ "coverage": "bun test --coverage",
81
+ "build:publish": "tsup --no-sourcemap --minify",
82
+ "dev": "tsup --watch",
83
+ "clean": "rm -rf dist",
84
+ "typecheck": "tsc --noEmit",
85
+ "release": "pnpm run prepublishOnly && npm publish --access public",
86
+ "release:dry": "pnpm run prepublishOnly && npm pack --dry-run"
86
87
  }
87
- }
88
+ }