@dexterai/x402 1.6.2 → 1.6.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.
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/server/index.cjs +30 -5
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.js +30 -5
- package/dist/server/index.js.map +1 -1
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
package/dist/utils/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/index.ts","../../src/types.ts","../../src/utils.ts"],"sourcesContent":["/**\n * @dexterai/x402 Utils\n *\n * Helper functions for x402 payments.\n *\n * @example\n * ```typescript\n * import { toAtomicUnits, fromAtomicUnits } from '@dexterai/x402/utils';\n *\n * const atomic = toAtomicUnits(0.05, 6); // '50000'\n * const human = fromAtomicUnits('50000', 6); // 0.05\n * ```\n */\n\nexport {\n toAtomicUnits,\n fromAtomicUnits,\n getChainFamily,\n getChainName,\n getExplorerUrl,\n type ChainFamily,\n} from '../utils';\n","/**\n * x402 v2 SDK — Shared Types\n *\n * Chain-agnostic types for x402 v2 payments.\n * Works with Solana, Base, and any future x402-compatible networks.\n */\n\n// ============================================================================\n// Network Constants\n// ============================================================================\n\n/** CAIP-2 network identifier for Solana mainnet */\nexport const SOLANA_MAINNET_NETWORK = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp';\n\n/** CAIP-2 network identifier for Base mainnet */\nexport const BASE_MAINNET_NETWORK = 'eip155:8453';\n\n/** Alias for Solana mainnet */\nexport const SOLANA_MAINNET = SOLANA_MAINNET_NETWORK;\n\n/** Alias for Base mainnet */\nexport const BASE_MAINNET = BASE_MAINNET_NETWORK;\n\n// ============================================================================\n// Asset Constants\n// ============================================================================\n\n/** USDC mint on Solana mainnet */\nexport const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';\n\n/** USDC address on Base mainnet */\nexport const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';\n\n// ============================================================================\n// Facilitator Constants\n// ============================================================================\n\n/** Dexter's public x402 v2 facilitator URL */\nexport const DEXTER_FACILITATOR_URL = 'https://x402.dexter.cash';\n\n// ============================================================================\n// Payment Types\n// ============================================================================\n\n/**\n * Asset configuration for payments\n */\nexport interface AssetConfig {\n /** Token address (mint on Solana, contract on EVM) */\n address: string;\n /** Token decimals */\n decimals: number;\n /** Optional: Human-readable symbol */\n symbol?: string;\n}\n\n/**\n * Resource info included in payment requirements\n */\nexport interface ResourceInfo {\n /** Resource URL */\n url: string;\n /** Human-readable description */\n description?: string;\n /** MIME type of the resource */\n mimeType?: string;\n}\n\n/**\n * Extra fields in payment requirements\n * Chain-specific fields may vary\n */\nexport interface AcceptsExtra {\n /** Facilitator address that pays tx fees (required for Solana) */\n feePayer?: string;\n /** Token decimals (optional - defaults to 6 for USDC) */\n decimals?: number;\n /** EIP-712: Token name (EVM only) */\n name?: string;\n /** EIP-712: Token version (EVM only) */\n version?: string;\n /** Additional chain-specific fields */\n [key: string]: unknown;\n}\n\n/**\n * A single payment option in the accepts array\n */\nexport interface PaymentAccept {\n /** x402 version (1 or 2, defaults to 2 if not specified) */\n x402Version?: 1 | 2;\n /** Payment scheme (always 'exact' for x402 v2) */\n scheme: 'exact';\n /** CAIP-2 network identifier (v1: 'solana', v2: 'solana:5eykt...') */\n network: string;\n /** Payment amount in atomic units (x402 spec field - REQUIRED) */\n maxAmountRequired: string;\n /** Alias for maxAmountRequired (for convenience) */\n amount?: string;\n /** Token address */\n asset: string;\n /** Seller's address to receive payment */\n payTo: string;\n /** Maximum seconds until payment expires */\n maxTimeoutSeconds: number;\n /** Chain-specific extra data */\n extra: AcceptsExtra;\n}\n\n/**\n * Full PaymentRequired structure (sent in PAYMENT-REQUIRED header)\n */\nexport interface PaymentRequired {\n /** x402 version (always 2) */\n x402Version: 2;\n /** Resource being accessed */\n resource: ResourceInfo;\n /** Available payment options */\n accepts: PaymentAccept[];\n /** Optional error message */\n error?: string;\n}\n\n/**\n * PaymentSignature structure (sent in PAYMENT-SIGNATURE header)\n */\nexport interface PaymentSignature {\n /** x402 version (always 2) */\n x402Version: 2;\n /** Resource being accessed */\n resource: ResourceInfo;\n /** The payment option that was accepted */\n accepted: PaymentAccept;\n /** The signed payment */\n payload: {\n /** Signed transaction (base64 for Solana, JSON for EVM) */\n transaction: string;\n };\n}\n\n// ============================================================================\n// Facilitator Response Types\n// ============================================================================\n\n/**\n * Response from /verify endpoint\n */\nexport interface VerifyResponse {\n /** Whether the payment is valid */\n isValid: boolean;\n /** Reason for invalidity (if invalid) */\n invalidReason?: string;\n /** Payer address */\n payer?: string;\n}\n\n/**\n * Response from /settle endpoint\n */\nexport interface SettleResponse {\n /** Whether settlement succeeded */\n success: boolean;\n /** Transaction signature/hash */\n transaction?: string;\n /** Network the payment was made on */\n network: string;\n /** Error reason (if failed) */\n errorReason?: string;\n /** Error code (if failed) */\n errorCode?: string;\n /** Payer address */\n payer?: string;\n}\n\n// ============================================================================\n// Access Pass Types\n// ============================================================================\n\n/**\n * A single access pass tier offered by a seller\n */\nexport interface AccessPassTier {\n /** Tier ID (e.g., '1h', '24h') */\n id: string;\n /** Human-readable label (e.g., '1 hour') */\n label: string;\n /** Duration in seconds */\n seconds: number;\n /** Price in USD (e.g., '0.50') */\n price: string;\n /** Price in atomic units (e.g., '500000') */\n priceAtomic: string;\n}\n\n/**\n * Access pass info returned in X-ACCESS-PASS-TIERS header\n */\nexport interface AccessPassInfo {\n /** Available tiers (if tier-based pricing) */\n tiers?: AccessPassTier[];\n /** Rate per hour in USD (if custom duration pricing) */\n ratePerHour?: string;\n /** Pass issuer identifier */\n issuer?: string;\n}\n\n/**\n * JWT claims inside an access pass token\n */\nexport interface AccessPassClaims {\n /** Subject — always 'x402-access-pass' */\n sub: string;\n /** Tier ID or 'custom' */\n tier: string;\n /** Duration in seconds */\n duration: number;\n /** Issued at (unix seconds) */\n iat: number;\n /** Expires at (unix seconds) */\n exp: number;\n /** Payer wallet address */\n payer: string;\n /** Network used for payment */\n network: string;\n /** Issuer identifier */\n iss: string;\n}\n\n/**\n * Client-side access pass configuration\n */\nexport interface AccessPassClientConfig {\n /** Enable access pass mode (default: true when this config is present) */\n enabled?: boolean;\n /** Preferred tier ID (e.g., '1h') — pick this tier if available */\n preferTier?: string;\n /** Preferred custom duration in seconds (e.g., 3600) */\n preferDuration?: number;\n /** Maximum amount willing to spend in USD (e.g., '2.00') */\n maxSpend?: string;\n /** Auto-renew expired passes (default: true) */\n autoRenew?: boolean;\n}\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\n/**\n * SDK error codes\n */\nexport type X402ErrorCode =\n // Client errors\n | 'missing_payment_required_header'\n | 'invalid_payment_required'\n | 'unsupported_network'\n | 'no_matching_payment_option'\n | 'no_solana_accept' // Legacy, kept for compatibility\n | 'missing_fee_payer'\n | 'missing_decimals'\n | 'missing_amount'\n | 'amount_exceeds_max'\n | 'insufficient_balance'\n | 'wallet_missing_sign_transaction'\n | 'wallet_not_connected'\n | 'transaction_build_failed'\n | 'payment_rejected'\n // Server errors\n | 'invalid_payment_signature'\n | 'facilitator_verify_failed'\n | 'facilitator_settle_failed'\n | 'facilitator_request_failed'\n | 'no_matching_requirement'\n // Access pass errors\n | 'access_pass_expired'\n | 'access_pass_invalid'\n | 'access_pass_tier_not_found'\n | 'access_pass_exceeds_max_spend';\n\n/**\n * Custom error class for x402 operations\n */\nexport class X402Error extends Error {\n /** Error code for programmatic handling */\n code: X402ErrorCode;\n /** Additional error details */\n details?: unknown;\n\n constructor(code: X402ErrorCode, message: string, details?: unknown) {\n super(message);\n this.name = 'X402Error';\n this.code = code;\n this.details = details;\n // Maintain proper prototype chain\n Object.setPrototypeOf(this, X402Error.prototype);\n }\n}\n","/**\n * Utility Functions\n *\n * Chain-agnostic helpers for x402 payments.\n */\n\nimport { SOLANA_MAINNET_NETWORK, BASE_MAINNET_NETWORK } from './types';\n\n// ============================================================================\n// Amount Conversion\n// ============================================================================\n\n/**\n * Convert human-readable amount to atomic units\n *\n * @param amount - Human-readable amount (e.g., 0.05 for $0.05)\n * @param decimals - Token decimals (e.g., 6 for USDC)\n * @returns Amount in atomic units as string\n *\n * @example\n * ```typescript\n * toAtomicUnits(0.05, 6) // '50000'\n * toAtomicUnits(1.50, 6) // '1500000'\n * ```\n */\nexport function toAtomicUnits(amount: number, decimals: number): string {\n const multiplier = Math.pow(10, decimals);\n return Math.floor(amount * multiplier).toString();\n}\n\n/**\n * Convert atomic units to human-readable amount\n *\n * @param atomicUnits - Amount in smallest units\n * @param decimals - Token decimals\n * @returns Human-readable amount\n *\n * @example\n * ```typescript\n * fromAtomicUnits('50000', 6) // 0.05\n * fromAtomicUnits(1500000n, 6) // 1.5\n * ```\n */\nexport function fromAtomicUnits(\n atomicUnits: string | bigint | number,\n decimals: number\n): number {\n const divisor = Math.pow(10, decimals);\n return Number(atomicUnits) / divisor;\n}\n\n// ============================================================================\n// Network Helpers\n// ============================================================================\n\n/**\n * Network type\n */\nexport type ChainFamily = 'solana' | 'evm' | 'unknown';\n\n/**\n * Get the chain family from a CAIP-2 network identifier\n *\n * @param network - CAIP-2 network identifier\n * @returns Chain family\n *\n * @example\n * ```typescript\n * getChainFamily('solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp') // 'solana'\n * getChainFamily('eip155:8453') // 'evm'\n * ```\n */\nexport function getChainFamily(network: string): ChainFamily {\n if (network.startsWith('solana:') || network === 'solana') {\n return 'solana';\n }\n if (network.startsWith('eip155:') || ['base', 'ethereum', 'arbitrum'].includes(network)) {\n return 'evm';\n }\n return 'unknown';\n}\n\n/**\n * Get default RPC URL for a network\n *\n * @param network - CAIP-2 network identifier\n * @returns Default RPC URL\n */\nexport function getDefaultRpcUrl(network: string): string {\n const family = getChainFamily(network);\n\n if (family === 'solana') {\n if (network.includes('devnet') || network === 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1') {\n return 'https://api.devnet.solana.com';\n }\n if (network.includes('testnet') || network === 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z') {\n return 'https://api.testnet.solana.com';\n }\n // Mainnet uses Dexter's RPC proxy\n return 'https://api.dexter.cash/api/solana/rpc';\n }\n\n if (family === 'evm') {\n // Extract chain ID from CAIP-2\n if (network.startsWith('eip155:')) {\n const chainId = network.split(':')[1];\n switch (chainId) {\n case '8453': return 'https://api.dexter.cash/api/base/rpc'; // Dexter proxy\n case '84532': return 'https://sepolia.base.org';\n case '1': return 'https://eth.llamarpc.com';\n case '42161': return 'https://arb1.arbitrum.io/rpc';\n default: return 'https://api.dexter.cash/api/base/rpc';\n }\n }\n // Legacy names\n if (network === 'base') return 'https://api.dexter.cash/api/base/rpc';\n if (network === 'ethereum') return 'https://eth.llamarpc.com';\n if (network === 'arbitrum') return 'https://arb1.arbitrum.io/rpc';\n return 'https://api.dexter.cash/api/base/rpc';\n }\n\n // Unknown - return Dexter's Solana proxy\n return 'https://api.dexter.cash/api/solana/rpc';\n}\n\n/**\n * Get human-readable chain name\n *\n * @param network - CAIP-2 network identifier\n * @returns Human-readable name\n */\nexport function getChainName(network: string): string {\n const mapping: Record<string, string> = {\n [SOLANA_MAINNET_NETWORK]: 'Solana',\n 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1': 'Solana Devnet',\n 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z': 'Solana Testnet',\n 'solana': 'Solana',\n [BASE_MAINNET_NETWORK]: 'Base',\n 'eip155:84532': 'Base Sepolia',\n 'eip155:1': 'Ethereum',\n 'eip155:42161': 'Arbitrum One',\n 'base': 'Base',\n 'ethereum': 'Ethereum',\n 'arbitrum': 'Arbitrum',\n };\n return mapping[network] || network;\n}\n\n// ============================================================================\n// Transaction URL Helpers\n// ============================================================================\n\n/**\n * Get explorer URL for a transaction\n *\n * @param txSignature - Transaction signature/hash\n * @param network - CAIP-2 network identifier\n * @returns Explorer URL\n */\nexport function getExplorerUrl(txSignature: string, network: string): string {\n const family = getChainFamily(network);\n\n if (family === 'solana') {\n const isDevnet = network.includes('devnet') || network === 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1';\n if (isDevnet) {\n return `https://solscan.io/tx/${txSignature}?cluster=devnet`;\n }\n // Prefer Orb Markets for mainnet\n return `https://www.orbmarkets.io/tx/${txSignature}`;\n }\n\n if (family === 'evm') {\n // Extract chain ID\n let chainId = '8453'; // Default to Base\n if (network.startsWith('eip155:')) {\n chainId = network.split(':')[1];\n } else if (network === 'ethereum') {\n chainId = '1';\n } else if (network === 'arbitrum') {\n chainId = '42161';\n }\n\n switch (chainId) {\n case '8453': return `https://basescan.org/tx/${txSignature}`;\n case '84532': return `https://sepolia.basescan.org/tx/${txSignature}`;\n case '1': return `https://etherscan.io/tx/${txSignature}`;\n case '42161': return `https://arbiscan.io/tx/${txSignature}`;\n default: return `https://basescan.org/tx/${txSignature}`;\n }\n }\n\n return `https://solscan.io/tx/${txSignature}`;\n}\n\n// ============================================================================\n// Encoding Helpers\n// ============================================================================\n\n/**\n * Encode an object as base64 JSON\n */\nexport function encodeBase64Json(obj: unknown): string {\n return btoa(JSON.stringify(obj));\n}\n\n/**\n * Decode base64 JSON to object\n */\nexport function decodeBase64Json<T>(encoded: string): T {\n return JSON.parse(atob(encoded)) as T;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACYO,IAAM,yBAAyB;AAG/B,IAAM,uBAAuB;;;ACU7B,SAAS,cAAc,QAAgB,UAA0B;AACtE,QAAM,aAAa,KAAK,IAAI,IAAI,QAAQ;AACxC,SAAO,KAAK,MAAM,SAAS,UAAU,EAAE,SAAS;AAClD;AAeO,SAAS,gBACd,aACA,UACQ;AACR,QAAM,UAAU,KAAK,IAAI,IAAI,QAAQ;AACrC,SAAO,OAAO,WAAW,IAAI;AAC/B;AAuBO,SAAS,eAAe,SAA8B;AAC3D,MAAI,QAAQ,WAAW,SAAS,KAAK,YAAY,UAAU;AACzD,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,WAAW,SAAS,KAAK,CAAC,QAAQ,YAAY,UAAU,EAAE,SAAS,OAAO,GAAG;AACvF,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAmDO,SAAS,aAAa,SAAyB;AACpD,QAAM,UAAkC;AAAA,IACtC,CAAC,sBAAsB,GAAG;AAAA,IAC1B,2CAA2C;AAAA,IAC3C,2CAA2C;AAAA,IAC3C,UAAU;AAAA,IACV,CAAC,oBAAoB,GAAG;AAAA,IACxB,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AACA,SAAO,QAAQ,OAAO,KAAK;AAC7B;AAaO,SAAS,eAAe,aAAqB,SAAyB;AAC3E,QAAM,SAAS,eAAe,OAAO;AAErC,MAAI,WAAW,UAAU;AACvB,UAAM,WAAW,QAAQ,SAAS,QAAQ,KAAK,YAAY;AAC3D,QAAI,UAAU;AACZ,aAAO,yBAAyB,WAAW;AAAA,IAC7C;AAEA,WAAO,gCAAgC,WAAW;AAAA,EACpD;AAEA,MAAI,WAAW,OAAO;AAEpB,QAAI,UAAU;AACd,QAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,gBAAU,QAAQ,MAAM,GAAG,EAAE,CAAC;AAAA,IAChC,WAAW,YAAY,YAAY;AACjC,gBAAU;AAAA,IACZ,WAAW,YAAY,YAAY;AACjC,gBAAU;AAAA,IACZ;AAEA,YAAQ,SAAS;AAAA,MACf,KAAK;AAAQ,eAAO,2BAA2B,WAAW;AAAA,MAC1D,KAAK;AAAS,eAAO,mCAAmC,WAAW;AAAA,MACnE,KAAK;AAAK,eAAO,2BAA2B,WAAW;AAAA,MACvD,KAAK;AAAS,eAAO,0BAA0B,WAAW;AAAA,MAC1D;AAAS,eAAO,2BAA2B,WAAW;AAAA,IACxD;AAAA,EACF;AAEA,SAAO,yBAAyB,WAAW;AAC7C;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/index.ts","../../src/types.ts","../../src/utils.ts"],"sourcesContent":["/**\n * @dexterai/x402 Utils\n *\n * Helper functions for x402 payments.\n *\n * @example\n * ```typescript\n * import { toAtomicUnits, fromAtomicUnits } from '@dexterai/x402/utils';\n *\n * const atomic = toAtomicUnits(0.05, 6); // '50000'\n * const human = fromAtomicUnits('50000', 6); // 0.05\n * ```\n */\n\nexport {\n toAtomicUnits,\n fromAtomicUnits,\n getChainFamily,\n getChainName,\n getExplorerUrl,\n type ChainFamily,\n} from '../utils';\n","/**\n * x402 v2 SDK — Shared Types\n *\n * Chain-agnostic types for x402 v2 payments.\n * Works with Solana, Base, and any future x402-compatible networks.\n */\n\n// ============================================================================\n// Network Constants\n// ============================================================================\n\n/** CAIP-2 network identifier for Solana mainnet */\nexport const SOLANA_MAINNET_NETWORK = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp';\n\n/** CAIP-2 network identifier for Base mainnet */\nexport const BASE_MAINNET_NETWORK = 'eip155:8453';\n\n/** Alias for Solana mainnet */\nexport const SOLANA_MAINNET = SOLANA_MAINNET_NETWORK;\n\n/** Alias for Base mainnet */\nexport const BASE_MAINNET = BASE_MAINNET_NETWORK;\n\n// ============================================================================\n// Asset Constants\n// ============================================================================\n\n/** USDC mint on Solana mainnet */\nexport const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';\n\n/** USDC address on Base mainnet */\nexport const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';\n\n// ============================================================================\n// Facilitator Constants\n// ============================================================================\n\n/** Dexter's public x402 v2 facilitator URL */\nexport const DEXTER_FACILITATOR_URL = 'https://x402.dexter.cash';\n\n// ============================================================================\n// Payment Types\n// ============================================================================\n\n/**\n * Asset configuration for payments\n */\nexport interface AssetConfig {\n /** Token address (mint on Solana, contract on EVM) */\n address: string;\n /** Token decimals */\n decimals: number;\n /** Optional: Human-readable symbol */\n symbol?: string;\n}\n\n/**\n * Resource info included in payment requirements\n */\nexport interface ResourceInfo {\n /** Resource URL */\n url: string;\n /** Human-readable description */\n description?: string;\n /** MIME type of the resource */\n mimeType?: string;\n}\n\n/**\n * Extra fields in payment requirements\n * Chain-specific fields may vary\n */\nexport interface AcceptsExtra {\n /** Facilitator address that pays tx fees (required for Solana) */\n feePayer?: string;\n /** Token decimals (optional - defaults to 6 for USDC) */\n decimals?: number;\n /** EIP-712: Token name (EVM only) */\n name?: string;\n /** EIP-712: Token version (EVM only) */\n version?: string;\n /** Additional chain-specific fields */\n [key: string]: unknown;\n}\n\n/**\n * A single payment option in the accepts array\n */\nexport interface PaymentAccept {\n /** x402 version (1 or 2, defaults to 2 if not specified) */\n x402Version?: 1 | 2;\n /** Payment scheme (always 'exact' for x402 v2) */\n scheme: 'exact';\n /** CAIP-2 network identifier (v1: 'solana', v2: 'solana:5eykt...') */\n network: string;\n /** Payment amount in atomic units (x402 spec field - REQUIRED) */\n maxAmountRequired: string;\n /** Alias for maxAmountRequired (for convenience) */\n amount?: string;\n /** Token address */\n asset: string;\n /** Seller's address to receive payment */\n payTo: string;\n /** Maximum seconds until payment expires */\n maxTimeoutSeconds: number;\n /** Chain-specific extra data */\n extra: AcceptsExtra;\n}\n\n/**\n * Full PaymentRequired structure (sent in PAYMENT-REQUIRED header)\n */\nexport interface PaymentRequired {\n /** x402 version (always 2) */\n x402Version: 2;\n /** Resource being accessed */\n resource: ResourceInfo;\n /** Available payment options */\n accepts: PaymentAccept[];\n /** Optional error message */\n error?: string;\n}\n\n/**\n * PaymentSignature structure (sent in PAYMENT-SIGNATURE header)\n */\nexport interface PaymentSignature {\n /** x402 version (always 2) */\n x402Version: 2;\n /** Resource being accessed */\n resource: ResourceInfo;\n /** The payment option that was accepted */\n accepted: PaymentAccept;\n /** The signed payment */\n payload: {\n /** Signed transaction (base64 for Solana, JSON for EVM) */\n transaction: string;\n };\n}\n\n// ============================================================================\n// Facilitator Response Types\n// ============================================================================\n\n/**\n * Response from /verify endpoint\n */\nexport interface VerifyResponse {\n /** Whether the payment is valid */\n isValid: boolean;\n /** Reason for invalidity (if invalid) */\n invalidReason?: string;\n /** Payer address */\n payer?: string;\n}\n\n/**\n * Response from /settle endpoint\n */\nexport interface SettleResponse {\n /** Whether settlement succeeded */\n success: boolean;\n /** Transaction signature/hash */\n transaction?: string;\n /** Network the payment was made on */\n network: string;\n /** Error reason (if failed) */\n errorReason?: string;\n /** Error code (if failed) */\n errorCode?: string;\n /** Payer address */\n payer?: string;\n}\n\n// ============================================================================\n// Access Pass Types\n// ============================================================================\n\n/**\n * A single access pass tier offered by a seller\n */\nexport interface AccessPassTier {\n /** Tier ID (e.g., '1h', '24h') */\n id: string;\n /** Human-readable label (e.g., '1 hour') */\n label: string;\n /** Duration in seconds */\n seconds: number;\n /** Price in USD (e.g., '0.50') */\n price: string;\n /** Price in atomic units (e.g., '500000') */\n priceAtomic: string;\n}\n\n/**\n * Access pass info returned in X-ACCESS-PASS-TIERS header\n */\nexport interface AccessPassInfo {\n /** Available tiers (if tier-based pricing) */\n tiers?: AccessPassTier[];\n /** Rate per hour in USD (if custom duration pricing) */\n ratePerHour?: string;\n /** Pass issuer identifier */\n issuer?: string;\n}\n\n/**\n * JWT claims inside an access pass token\n */\nexport interface AccessPassClaims {\n /** Subject — always 'x402-access-pass' */\n sub: string;\n /** Tier ID or 'custom' */\n tier: string;\n /** Duration in seconds */\n duration: number;\n /** Issued at (unix seconds) */\n iat: number;\n /** Expires at (unix seconds) */\n exp: number;\n /** Payer wallet address */\n payer: string;\n /** Network used for payment */\n network: string;\n /** Issuer identifier */\n iss: string;\n}\n\n/**\n * Client-side access pass configuration\n */\nexport interface AccessPassClientConfig {\n /** Enable access pass mode (default: true when this config is present) */\n enabled?: boolean;\n /** Preferred tier ID (e.g., '1h') — pick this tier if available */\n preferTier?: string;\n /** Preferred custom duration in seconds (e.g., 3600) */\n preferDuration?: number;\n /** Maximum amount willing to spend in USD (e.g., '2.00') */\n maxSpend?: string;\n /** Auto-renew expired passes (default: true) */\n autoRenew?: boolean;\n}\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\n/**\n * SDK error codes\n */\nexport type X402ErrorCode =\n // Client errors\n | 'missing_payment_required_header'\n | 'invalid_payment_required'\n | 'unsupported_network'\n | 'no_matching_payment_option'\n | 'no_solana_accept' // Legacy, kept for compatibility\n | 'missing_fee_payer'\n | 'missing_decimals'\n | 'missing_amount'\n | 'amount_exceeds_max'\n | 'insufficient_balance'\n | 'wallet_missing_sign_transaction'\n | 'wallet_not_connected'\n | 'transaction_build_failed'\n | 'payment_rejected'\n // Server errors\n | 'invalid_payment_signature'\n | 'facilitator_verify_failed'\n | 'facilitator_settle_failed'\n | 'facilitator_request_failed'\n | 'no_matching_requirement'\n // Access pass errors\n | 'access_pass_expired'\n | 'access_pass_invalid'\n | 'access_pass_tier_not_found'\n | 'access_pass_exceeds_max_spend';\n\n/**\n * Custom error class for x402 operations\n */\nexport class X402Error extends Error {\n /** Error code for programmatic handling */\n code: X402ErrorCode;\n /** Additional error details */\n details?: unknown;\n\n constructor(code: X402ErrorCode, message: string, details?: unknown) {\n super(message);\n this.name = 'X402Error';\n this.code = code;\n this.details = details;\n // Maintain proper prototype chain\n Object.setPrototypeOf(this, X402Error.prototype);\n }\n}\n","/**\n * Utility Functions\n *\n * Chain-agnostic helpers for x402 payments.\n */\n\nimport { SOLANA_MAINNET_NETWORK, BASE_MAINNET_NETWORK } from './types';\n\n// ============================================================================\n// Amount Conversion\n// ============================================================================\n\n/**\n * Convert human-readable amount to atomic units\n *\n * @param amount - Human-readable amount (e.g., 0.05 for $0.05)\n * @param decimals - Token decimals (e.g., 6 for USDC)\n * @returns Amount in atomic units as string\n *\n * @example\n * ```typescript\n * toAtomicUnits(0.05, 6) // '50000'\n * toAtomicUnits(1.50, 6) // '1500000'\n * ```\n */\nexport function toAtomicUnits(amount: number, decimals: number): string {\n const multiplier = Math.pow(10, decimals);\n return Math.floor(amount * multiplier).toString();\n}\n\n/**\n * Convert atomic units to human-readable amount\n *\n * @param atomicUnits - Amount in smallest units\n * @param decimals - Token decimals\n * @returns Human-readable amount\n *\n * @example\n * ```typescript\n * fromAtomicUnits('50000', 6) // 0.05\n * fromAtomicUnits(1500000n, 6) // 1.5\n * ```\n */\nexport function fromAtomicUnits(\n atomicUnits: string | bigint | number,\n decimals: number\n): number {\n const divisor = Math.pow(10, decimals);\n return Number(atomicUnits) / divisor;\n}\n\n// ============================================================================\n// Network Helpers\n// ============================================================================\n\n/**\n * Network type\n */\nexport type ChainFamily = 'solana' | 'evm' | 'unknown';\n\n/**\n * Get the chain family from a CAIP-2 network identifier\n *\n * @param network - CAIP-2 network identifier\n * @returns Chain family\n *\n * @example\n * ```typescript\n * getChainFamily('solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp') // 'solana'\n * getChainFamily('eip155:8453') // 'evm'\n * ```\n */\nexport function getChainFamily(network: string): ChainFamily {\n if (network.startsWith('solana:') || network === 'solana') {\n return 'solana';\n }\n if (network.startsWith('eip155:') || ['base', 'ethereum', 'arbitrum'].includes(network)) {\n return 'evm';\n }\n return 'unknown';\n}\n\n/**\n * Get default RPC URL for a network\n *\n * @param network - CAIP-2 network identifier\n * @returns Default RPC URL\n */\nexport function getDefaultRpcUrl(network: string): string {\n const family = getChainFamily(network);\n\n if (family === 'solana') {\n if (network.includes('devnet') || network === 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1') {\n return 'https://api.devnet.solana.com';\n }\n if (network.includes('testnet') || network === 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z') {\n return 'https://api.testnet.solana.com';\n }\n // Mainnet uses Dexter's RPC proxy\n return 'https://api.dexter.cash/api/solana/rpc';\n }\n\n if (family === 'evm') {\n // Extract chain ID from CAIP-2\n if (network.startsWith('eip155:')) {\n const chainId = network.split(':')[1];\n switch (chainId) {\n case '8453': return 'https://api.dexter.cash/api/base/rpc'; // Dexter proxy\n case '84532': return 'https://sepolia.base.org';\n case '1': return 'https://eth.llamarpc.com';\n case '42161': return 'https://arb1.arbitrum.io/rpc';\n default: return 'https://api.dexter.cash/api/base/rpc';\n }\n }\n // Legacy names\n if (network === 'base') return 'https://api.dexter.cash/api/base/rpc';\n if (network === 'ethereum') return 'https://eth.llamarpc.com';\n if (network === 'arbitrum') return 'https://arb1.arbitrum.io/rpc';\n return 'https://api.dexter.cash/api/base/rpc';\n }\n\n // Unknown - return Dexter's Solana proxy\n return 'https://api.dexter.cash/api/solana/rpc';\n}\n\n/**\n * Get human-readable chain name\n *\n * @param network - CAIP-2 network identifier\n * @returns Human-readable name\n */\nexport function getChainName(network: string): string {\n const mapping: Record<string, string> = {\n [SOLANA_MAINNET_NETWORK]: 'Solana',\n 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1': 'Solana Devnet',\n 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z': 'Solana Testnet',\n 'solana': 'Solana',\n [BASE_MAINNET_NETWORK]: 'Base',\n 'eip155:84532': 'Base Sepolia',\n 'eip155:1': 'Ethereum',\n 'eip155:42161': 'Arbitrum One',\n 'base': 'Base',\n 'ethereum': 'Ethereum',\n 'arbitrum': 'Arbitrum',\n };\n return mapping[network] || network;\n}\n\n// ============================================================================\n// Transaction URL Helpers\n// ============================================================================\n\n/**\n * Get explorer URL for a transaction\n *\n * @param txSignature - Transaction signature/hash\n * @param network - CAIP-2 network identifier\n * @returns Explorer URL\n */\nexport function getExplorerUrl(txSignature: string, network: string): string {\n const family = getChainFamily(network);\n\n if (family === 'solana') {\n const isDevnet = network.includes('devnet') || network === 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1';\n if (isDevnet) {\n return `https://solscan.io/tx/${txSignature}?cluster=devnet`;\n }\n // Prefer Orb Markets for mainnet\n return `https://www.orbmarkets.io/tx/${txSignature}`;\n }\n\n if (family === 'evm') {\n // Extract chain ID\n let chainId = '8453'; // Default to Base\n if (network.startsWith('eip155:')) {\n chainId = network.split(':')[1];\n } else if (network === 'ethereum') {\n chainId = '1';\n } else if (network === 'arbitrum') {\n chainId = '42161';\n }\n\n switch (chainId) {\n case '8453': return `https://basescan.org/tx/${txSignature}`;\n case '84532': return `https://sepolia.basescan.org/tx/${txSignature}`;\n case '1': return `https://etherscan.io/tx/${txSignature}`;\n case '42161': return `https://arbiscan.io/tx/${txSignature}`;\n default: return `https://basescan.org/tx/${txSignature}`;\n }\n }\n\n return `https://solscan.io/tx/${txSignature}`;\n}\n\n// ============================================================================\n// Encoding Helpers\n// ============================================================================\n\n/**\n * Unicode-safe base64 encode a string.\n * Works in both Node.js and browsers, handling characters above U+00FF\n * that would cause btoa() to throw InvalidCharacterError.\n */\nfunction safeBase64Encode(str: string): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(str, 'utf-8').toString('base64');\n }\n // Browser fallback: encode UTF-8 bytes via TextEncoder\n const bytes = new TextEncoder().encode(str);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n\n/**\n * Unicode-safe base64 decode a string.\n * Works in both Node.js and browsers.\n */\nfunction safeBase64Decode(encoded: string): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(encoded, 'base64').toString('utf-8');\n }\n // Browser fallback: decode via atob then UTF-8\n const binary = atob(encoded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return new TextDecoder().decode(bytes);\n}\n\n/**\n * Encode an object as base64 JSON (Unicode-safe)\n */\nexport function encodeBase64Json(obj: unknown): string {\n return safeBase64Encode(JSON.stringify(obj));\n}\n\n/**\n * Decode base64 JSON to object (Unicode-safe)\n */\nexport function decodeBase64Json<T>(encoded: string): T {\n return JSON.parse(safeBase64Decode(encoded)) as T;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACYO,IAAM,yBAAyB;AAG/B,IAAM,uBAAuB;;;ACU7B,SAAS,cAAc,QAAgB,UAA0B;AACtE,QAAM,aAAa,KAAK,IAAI,IAAI,QAAQ;AACxC,SAAO,KAAK,MAAM,SAAS,UAAU,EAAE,SAAS;AAClD;AAeO,SAAS,gBACd,aACA,UACQ;AACR,QAAM,UAAU,KAAK,IAAI,IAAI,QAAQ;AACrC,SAAO,OAAO,WAAW,IAAI;AAC/B;AAuBO,SAAS,eAAe,SAA8B;AAC3D,MAAI,QAAQ,WAAW,SAAS,KAAK,YAAY,UAAU;AACzD,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,WAAW,SAAS,KAAK,CAAC,QAAQ,YAAY,UAAU,EAAE,SAAS,OAAO,GAAG;AACvF,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAmDO,SAAS,aAAa,SAAyB;AACpD,QAAM,UAAkC;AAAA,IACtC,CAAC,sBAAsB,GAAG;AAAA,IAC1B,2CAA2C;AAAA,IAC3C,2CAA2C;AAAA,IAC3C,UAAU;AAAA,IACV,CAAC,oBAAoB,GAAG;AAAA,IACxB,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AACA,SAAO,QAAQ,OAAO,KAAK;AAC7B;AAaO,SAAS,eAAe,aAAqB,SAAyB;AAC3E,QAAM,SAAS,eAAe,OAAO;AAErC,MAAI,WAAW,UAAU;AACvB,UAAM,WAAW,QAAQ,SAAS,QAAQ,KAAK,YAAY;AAC3D,QAAI,UAAU;AACZ,aAAO,yBAAyB,WAAW;AAAA,IAC7C;AAEA,WAAO,gCAAgC,WAAW;AAAA,EACpD;AAEA,MAAI,WAAW,OAAO;AAEpB,QAAI,UAAU;AACd,QAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,gBAAU,QAAQ,MAAM,GAAG,EAAE,CAAC;AAAA,IAChC,WAAW,YAAY,YAAY;AACjC,gBAAU;AAAA,IACZ,WAAW,YAAY,YAAY;AACjC,gBAAU;AAAA,IACZ;AAEA,YAAQ,SAAS;AAAA,MACf,KAAK;AAAQ,eAAO,2BAA2B,WAAW;AAAA,MAC1D,KAAK;AAAS,eAAO,mCAAmC,WAAW;AAAA,MACnE,KAAK;AAAK,eAAO,2BAA2B,WAAW;AAAA,MACvD,KAAK;AAAS,eAAO,0BAA0B,WAAW;AAAA,MAC1D;AAAS,eAAO,2BAA2B,WAAW;AAAA,IACxD;AAAA,EACF;AAEA,SAAO,yBAAyB,WAAW;AAC7C;","names":[]}
|
package/dist/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/types.ts","../../src/utils.ts"],"sourcesContent":["/**\n * x402 v2 SDK — Shared Types\n *\n * Chain-agnostic types for x402 v2 payments.\n * Works with Solana, Base, and any future x402-compatible networks.\n */\n\n// ============================================================================\n// Network Constants\n// ============================================================================\n\n/** CAIP-2 network identifier for Solana mainnet */\nexport const SOLANA_MAINNET_NETWORK = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp';\n\n/** CAIP-2 network identifier for Base mainnet */\nexport const BASE_MAINNET_NETWORK = 'eip155:8453';\n\n/** Alias for Solana mainnet */\nexport const SOLANA_MAINNET = SOLANA_MAINNET_NETWORK;\n\n/** Alias for Base mainnet */\nexport const BASE_MAINNET = BASE_MAINNET_NETWORK;\n\n// ============================================================================\n// Asset Constants\n// ============================================================================\n\n/** USDC mint on Solana mainnet */\nexport const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';\n\n/** USDC address on Base mainnet */\nexport const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';\n\n// ============================================================================\n// Facilitator Constants\n// ============================================================================\n\n/** Dexter's public x402 v2 facilitator URL */\nexport const DEXTER_FACILITATOR_URL = 'https://x402.dexter.cash';\n\n// ============================================================================\n// Payment Types\n// ============================================================================\n\n/**\n * Asset configuration for payments\n */\nexport interface AssetConfig {\n /** Token address (mint on Solana, contract on EVM) */\n address: string;\n /** Token decimals */\n decimals: number;\n /** Optional: Human-readable symbol */\n symbol?: string;\n}\n\n/**\n * Resource info included in payment requirements\n */\nexport interface ResourceInfo {\n /** Resource URL */\n url: string;\n /** Human-readable description */\n description?: string;\n /** MIME type of the resource */\n mimeType?: string;\n}\n\n/**\n * Extra fields in payment requirements\n * Chain-specific fields may vary\n */\nexport interface AcceptsExtra {\n /** Facilitator address that pays tx fees (required for Solana) */\n feePayer?: string;\n /** Token decimals (optional - defaults to 6 for USDC) */\n decimals?: number;\n /** EIP-712: Token name (EVM only) */\n name?: string;\n /** EIP-712: Token version (EVM only) */\n version?: string;\n /** Additional chain-specific fields */\n [key: string]: unknown;\n}\n\n/**\n * A single payment option in the accepts array\n */\nexport interface PaymentAccept {\n /** x402 version (1 or 2, defaults to 2 if not specified) */\n x402Version?: 1 | 2;\n /** Payment scheme (always 'exact' for x402 v2) */\n scheme: 'exact';\n /** CAIP-2 network identifier (v1: 'solana', v2: 'solana:5eykt...') */\n network: string;\n /** Payment amount in atomic units (x402 spec field - REQUIRED) */\n maxAmountRequired: string;\n /** Alias for maxAmountRequired (for convenience) */\n amount?: string;\n /** Token address */\n asset: string;\n /** Seller's address to receive payment */\n payTo: string;\n /** Maximum seconds until payment expires */\n maxTimeoutSeconds: number;\n /** Chain-specific extra data */\n extra: AcceptsExtra;\n}\n\n/**\n * Full PaymentRequired structure (sent in PAYMENT-REQUIRED header)\n */\nexport interface PaymentRequired {\n /** x402 version (always 2) */\n x402Version: 2;\n /** Resource being accessed */\n resource: ResourceInfo;\n /** Available payment options */\n accepts: PaymentAccept[];\n /** Optional error message */\n error?: string;\n}\n\n/**\n * PaymentSignature structure (sent in PAYMENT-SIGNATURE header)\n */\nexport interface PaymentSignature {\n /** x402 version (always 2) */\n x402Version: 2;\n /** Resource being accessed */\n resource: ResourceInfo;\n /** The payment option that was accepted */\n accepted: PaymentAccept;\n /** The signed payment */\n payload: {\n /** Signed transaction (base64 for Solana, JSON for EVM) */\n transaction: string;\n };\n}\n\n// ============================================================================\n// Facilitator Response Types\n// ============================================================================\n\n/**\n * Response from /verify endpoint\n */\nexport interface VerifyResponse {\n /** Whether the payment is valid */\n isValid: boolean;\n /** Reason for invalidity (if invalid) */\n invalidReason?: string;\n /** Payer address */\n payer?: string;\n}\n\n/**\n * Response from /settle endpoint\n */\nexport interface SettleResponse {\n /** Whether settlement succeeded */\n success: boolean;\n /** Transaction signature/hash */\n transaction?: string;\n /** Network the payment was made on */\n network: string;\n /** Error reason (if failed) */\n errorReason?: string;\n /** Error code (if failed) */\n errorCode?: string;\n /** Payer address */\n payer?: string;\n}\n\n// ============================================================================\n// Access Pass Types\n// ============================================================================\n\n/**\n * A single access pass tier offered by a seller\n */\nexport interface AccessPassTier {\n /** Tier ID (e.g., '1h', '24h') */\n id: string;\n /** Human-readable label (e.g., '1 hour') */\n label: string;\n /** Duration in seconds */\n seconds: number;\n /** Price in USD (e.g., '0.50') */\n price: string;\n /** Price in atomic units (e.g., '500000') */\n priceAtomic: string;\n}\n\n/**\n * Access pass info returned in X-ACCESS-PASS-TIERS header\n */\nexport interface AccessPassInfo {\n /** Available tiers (if tier-based pricing) */\n tiers?: AccessPassTier[];\n /** Rate per hour in USD (if custom duration pricing) */\n ratePerHour?: string;\n /** Pass issuer identifier */\n issuer?: string;\n}\n\n/**\n * JWT claims inside an access pass token\n */\nexport interface AccessPassClaims {\n /** Subject — always 'x402-access-pass' */\n sub: string;\n /** Tier ID or 'custom' */\n tier: string;\n /** Duration in seconds */\n duration: number;\n /** Issued at (unix seconds) */\n iat: number;\n /** Expires at (unix seconds) */\n exp: number;\n /** Payer wallet address */\n payer: string;\n /** Network used for payment */\n network: string;\n /** Issuer identifier */\n iss: string;\n}\n\n/**\n * Client-side access pass configuration\n */\nexport interface AccessPassClientConfig {\n /** Enable access pass mode (default: true when this config is present) */\n enabled?: boolean;\n /** Preferred tier ID (e.g., '1h') — pick this tier if available */\n preferTier?: string;\n /** Preferred custom duration in seconds (e.g., 3600) */\n preferDuration?: number;\n /** Maximum amount willing to spend in USD (e.g., '2.00') */\n maxSpend?: string;\n /** Auto-renew expired passes (default: true) */\n autoRenew?: boolean;\n}\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\n/**\n * SDK error codes\n */\nexport type X402ErrorCode =\n // Client errors\n | 'missing_payment_required_header'\n | 'invalid_payment_required'\n | 'unsupported_network'\n | 'no_matching_payment_option'\n | 'no_solana_accept' // Legacy, kept for compatibility\n | 'missing_fee_payer'\n | 'missing_decimals'\n | 'missing_amount'\n | 'amount_exceeds_max'\n | 'insufficient_balance'\n | 'wallet_missing_sign_transaction'\n | 'wallet_not_connected'\n | 'transaction_build_failed'\n | 'payment_rejected'\n // Server errors\n | 'invalid_payment_signature'\n | 'facilitator_verify_failed'\n | 'facilitator_settle_failed'\n | 'facilitator_request_failed'\n | 'no_matching_requirement'\n // Access pass errors\n | 'access_pass_expired'\n | 'access_pass_invalid'\n | 'access_pass_tier_not_found'\n | 'access_pass_exceeds_max_spend';\n\n/**\n * Custom error class for x402 operations\n */\nexport class X402Error extends Error {\n /** Error code for programmatic handling */\n code: X402ErrorCode;\n /** Additional error details */\n details?: unknown;\n\n constructor(code: X402ErrorCode, message: string, details?: unknown) {\n super(message);\n this.name = 'X402Error';\n this.code = code;\n this.details = details;\n // Maintain proper prototype chain\n Object.setPrototypeOf(this, X402Error.prototype);\n }\n}\n","/**\n * Utility Functions\n *\n * Chain-agnostic helpers for x402 payments.\n */\n\nimport { SOLANA_MAINNET_NETWORK, BASE_MAINNET_NETWORK } from './types';\n\n// ============================================================================\n// Amount Conversion\n// ============================================================================\n\n/**\n * Convert human-readable amount to atomic units\n *\n * @param amount - Human-readable amount (e.g., 0.05 for $0.05)\n * @param decimals - Token decimals (e.g., 6 for USDC)\n * @returns Amount in atomic units as string\n *\n * @example\n * ```typescript\n * toAtomicUnits(0.05, 6) // '50000'\n * toAtomicUnits(1.50, 6) // '1500000'\n * ```\n */\nexport function toAtomicUnits(amount: number, decimals: number): string {\n const multiplier = Math.pow(10, decimals);\n return Math.floor(amount * multiplier).toString();\n}\n\n/**\n * Convert atomic units to human-readable amount\n *\n * @param atomicUnits - Amount in smallest units\n * @param decimals - Token decimals\n * @returns Human-readable amount\n *\n * @example\n * ```typescript\n * fromAtomicUnits('50000', 6) // 0.05\n * fromAtomicUnits(1500000n, 6) // 1.5\n * ```\n */\nexport function fromAtomicUnits(\n atomicUnits: string | bigint | number,\n decimals: number\n): number {\n const divisor = Math.pow(10, decimals);\n return Number(atomicUnits) / divisor;\n}\n\n// ============================================================================\n// Network Helpers\n// ============================================================================\n\n/**\n * Network type\n */\nexport type ChainFamily = 'solana' | 'evm' | 'unknown';\n\n/**\n * Get the chain family from a CAIP-2 network identifier\n *\n * @param network - CAIP-2 network identifier\n * @returns Chain family\n *\n * @example\n * ```typescript\n * getChainFamily('solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp') // 'solana'\n * getChainFamily('eip155:8453') // 'evm'\n * ```\n */\nexport function getChainFamily(network: string): ChainFamily {\n if (network.startsWith('solana:') || network === 'solana') {\n return 'solana';\n }\n if (network.startsWith('eip155:') || ['base', 'ethereum', 'arbitrum'].includes(network)) {\n return 'evm';\n }\n return 'unknown';\n}\n\n/**\n * Get default RPC URL for a network\n *\n * @param network - CAIP-2 network identifier\n * @returns Default RPC URL\n */\nexport function getDefaultRpcUrl(network: string): string {\n const family = getChainFamily(network);\n\n if (family === 'solana') {\n if (network.includes('devnet') || network === 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1') {\n return 'https://api.devnet.solana.com';\n }\n if (network.includes('testnet') || network === 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z') {\n return 'https://api.testnet.solana.com';\n }\n // Mainnet uses Dexter's RPC proxy\n return 'https://api.dexter.cash/api/solana/rpc';\n }\n\n if (family === 'evm') {\n // Extract chain ID from CAIP-2\n if (network.startsWith('eip155:')) {\n const chainId = network.split(':')[1];\n switch (chainId) {\n case '8453': return 'https://api.dexter.cash/api/base/rpc'; // Dexter proxy\n case '84532': return 'https://sepolia.base.org';\n case '1': return 'https://eth.llamarpc.com';\n case '42161': return 'https://arb1.arbitrum.io/rpc';\n default: return 'https://api.dexter.cash/api/base/rpc';\n }\n }\n // Legacy names\n if (network === 'base') return 'https://api.dexter.cash/api/base/rpc';\n if (network === 'ethereum') return 'https://eth.llamarpc.com';\n if (network === 'arbitrum') return 'https://arb1.arbitrum.io/rpc';\n return 'https://api.dexter.cash/api/base/rpc';\n }\n\n // Unknown - return Dexter's Solana proxy\n return 'https://api.dexter.cash/api/solana/rpc';\n}\n\n/**\n * Get human-readable chain name\n *\n * @param network - CAIP-2 network identifier\n * @returns Human-readable name\n */\nexport function getChainName(network: string): string {\n const mapping: Record<string, string> = {\n [SOLANA_MAINNET_NETWORK]: 'Solana',\n 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1': 'Solana Devnet',\n 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z': 'Solana Testnet',\n 'solana': 'Solana',\n [BASE_MAINNET_NETWORK]: 'Base',\n 'eip155:84532': 'Base Sepolia',\n 'eip155:1': 'Ethereum',\n 'eip155:42161': 'Arbitrum One',\n 'base': 'Base',\n 'ethereum': 'Ethereum',\n 'arbitrum': 'Arbitrum',\n };\n return mapping[network] || network;\n}\n\n// ============================================================================\n// Transaction URL Helpers\n// ============================================================================\n\n/**\n * Get explorer URL for a transaction\n *\n * @param txSignature - Transaction signature/hash\n * @param network - CAIP-2 network identifier\n * @returns Explorer URL\n */\nexport function getExplorerUrl(txSignature: string, network: string): string {\n const family = getChainFamily(network);\n\n if (family === 'solana') {\n const isDevnet = network.includes('devnet') || network === 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1';\n if (isDevnet) {\n return `https://solscan.io/tx/${txSignature}?cluster=devnet`;\n }\n // Prefer Orb Markets for mainnet\n return `https://www.orbmarkets.io/tx/${txSignature}`;\n }\n\n if (family === 'evm') {\n // Extract chain ID\n let chainId = '8453'; // Default to Base\n if (network.startsWith('eip155:')) {\n chainId = network.split(':')[1];\n } else if (network === 'ethereum') {\n chainId = '1';\n } else if (network === 'arbitrum') {\n chainId = '42161';\n }\n\n switch (chainId) {\n case '8453': return `https://basescan.org/tx/${txSignature}`;\n case '84532': return `https://sepolia.basescan.org/tx/${txSignature}`;\n case '1': return `https://etherscan.io/tx/${txSignature}`;\n case '42161': return `https://arbiscan.io/tx/${txSignature}`;\n default: return `https://basescan.org/tx/${txSignature}`;\n }\n }\n\n return `https://solscan.io/tx/${txSignature}`;\n}\n\n// ============================================================================\n// Encoding Helpers\n// ============================================================================\n\n/**\n * Encode an object as base64 JSON\n */\nexport function encodeBase64Json(obj: unknown): string {\n return btoa(JSON.stringify(obj));\n}\n\n/**\n * Decode base64 JSON to object\n */\nexport function decodeBase64Json<T>(encoded: string): T {\n return JSON.parse(atob(encoded)) as T;\n}\n"],"mappings":";AAYO,IAAM,yBAAyB;AAG/B,IAAM,uBAAuB;;;ACU7B,SAAS,cAAc,QAAgB,UAA0B;AACtE,QAAM,aAAa,KAAK,IAAI,IAAI,QAAQ;AACxC,SAAO,KAAK,MAAM,SAAS,UAAU,EAAE,SAAS;AAClD;AAeO,SAAS,gBACd,aACA,UACQ;AACR,QAAM,UAAU,KAAK,IAAI,IAAI,QAAQ;AACrC,SAAO,OAAO,WAAW,IAAI;AAC/B;AAuBO,SAAS,eAAe,SAA8B;AAC3D,MAAI,QAAQ,WAAW,SAAS,KAAK,YAAY,UAAU;AACzD,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,WAAW,SAAS,KAAK,CAAC,QAAQ,YAAY,UAAU,EAAE,SAAS,OAAO,GAAG;AACvF,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAmDO,SAAS,aAAa,SAAyB;AACpD,QAAM,UAAkC;AAAA,IACtC,CAAC,sBAAsB,GAAG;AAAA,IAC1B,2CAA2C;AAAA,IAC3C,2CAA2C;AAAA,IAC3C,UAAU;AAAA,IACV,CAAC,oBAAoB,GAAG;AAAA,IACxB,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AACA,SAAO,QAAQ,OAAO,KAAK;AAC7B;AAaO,SAAS,eAAe,aAAqB,SAAyB;AAC3E,QAAM,SAAS,eAAe,OAAO;AAErC,MAAI,WAAW,UAAU;AACvB,UAAM,WAAW,QAAQ,SAAS,QAAQ,KAAK,YAAY;AAC3D,QAAI,UAAU;AACZ,aAAO,yBAAyB,WAAW;AAAA,IAC7C;AAEA,WAAO,gCAAgC,WAAW;AAAA,EACpD;AAEA,MAAI,WAAW,OAAO;AAEpB,QAAI,UAAU;AACd,QAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,gBAAU,QAAQ,MAAM,GAAG,EAAE,CAAC;AAAA,IAChC,WAAW,YAAY,YAAY;AACjC,gBAAU;AAAA,IACZ,WAAW,YAAY,YAAY;AACjC,gBAAU;AAAA,IACZ;AAEA,YAAQ,SAAS;AAAA,MACf,KAAK;AAAQ,eAAO,2BAA2B,WAAW;AAAA,MAC1D,KAAK;AAAS,eAAO,mCAAmC,WAAW;AAAA,MACnE,KAAK;AAAK,eAAO,2BAA2B,WAAW;AAAA,MACvD,KAAK;AAAS,eAAO,0BAA0B,WAAW;AAAA,MAC1D;AAAS,eAAO,2BAA2B,WAAW;AAAA,IACxD;AAAA,EACF;AAEA,SAAO,yBAAyB,WAAW;AAC7C;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/types.ts","../../src/utils.ts"],"sourcesContent":["/**\n * x402 v2 SDK — Shared Types\n *\n * Chain-agnostic types for x402 v2 payments.\n * Works with Solana, Base, and any future x402-compatible networks.\n */\n\n// ============================================================================\n// Network Constants\n// ============================================================================\n\n/** CAIP-2 network identifier for Solana mainnet */\nexport const SOLANA_MAINNET_NETWORK = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp';\n\n/** CAIP-2 network identifier for Base mainnet */\nexport const BASE_MAINNET_NETWORK = 'eip155:8453';\n\n/** Alias for Solana mainnet */\nexport const SOLANA_MAINNET = SOLANA_MAINNET_NETWORK;\n\n/** Alias for Base mainnet */\nexport const BASE_MAINNET = BASE_MAINNET_NETWORK;\n\n// ============================================================================\n// Asset Constants\n// ============================================================================\n\n/** USDC mint on Solana mainnet */\nexport const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';\n\n/** USDC address on Base mainnet */\nexport const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';\n\n// ============================================================================\n// Facilitator Constants\n// ============================================================================\n\n/** Dexter's public x402 v2 facilitator URL */\nexport const DEXTER_FACILITATOR_URL = 'https://x402.dexter.cash';\n\n// ============================================================================\n// Payment Types\n// ============================================================================\n\n/**\n * Asset configuration for payments\n */\nexport interface AssetConfig {\n /** Token address (mint on Solana, contract on EVM) */\n address: string;\n /** Token decimals */\n decimals: number;\n /** Optional: Human-readable symbol */\n symbol?: string;\n}\n\n/**\n * Resource info included in payment requirements\n */\nexport interface ResourceInfo {\n /** Resource URL */\n url: string;\n /** Human-readable description */\n description?: string;\n /** MIME type of the resource */\n mimeType?: string;\n}\n\n/**\n * Extra fields in payment requirements\n * Chain-specific fields may vary\n */\nexport interface AcceptsExtra {\n /** Facilitator address that pays tx fees (required for Solana) */\n feePayer?: string;\n /** Token decimals (optional - defaults to 6 for USDC) */\n decimals?: number;\n /** EIP-712: Token name (EVM only) */\n name?: string;\n /** EIP-712: Token version (EVM only) */\n version?: string;\n /** Additional chain-specific fields */\n [key: string]: unknown;\n}\n\n/**\n * A single payment option in the accepts array\n */\nexport interface PaymentAccept {\n /** x402 version (1 or 2, defaults to 2 if not specified) */\n x402Version?: 1 | 2;\n /** Payment scheme (always 'exact' for x402 v2) */\n scheme: 'exact';\n /** CAIP-2 network identifier (v1: 'solana', v2: 'solana:5eykt...') */\n network: string;\n /** Payment amount in atomic units (x402 spec field - REQUIRED) */\n maxAmountRequired: string;\n /** Alias for maxAmountRequired (for convenience) */\n amount?: string;\n /** Token address */\n asset: string;\n /** Seller's address to receive payment */\n payTo: string;\n /** Maximum seconds until payment expires */\n maxTimeoutSeconds: number;\n /** Chain-specific extra data */\n extra: AcceptsExtra;\n}\n\n/**\n * Full PaymentRequired structure (sent in PAYMENT-REQUIRED header)\n */\nexport interface PaymentRequired {\n /** x402 version (always 2) */\n x402Version: 2;\n /** Resource being accessed */\n resource: ResourceInfo;\n /** Available payment options */\n accepts: PaymentAccept[];\n /** Optional error message */\n error?: string;\n}\n\n/**\n * PaymentSignature structure (sent in PAYMENT-SIGNATURE header)\n */\nexport interface PaymentSignature {\n /** x402 version (always 2) */\n x402Version: 2;\n /** Resource being accessed */\n resource: ResourceInfo;\n /** The payment option that was accepted */\n accepted: PaymentAccept;\n /** The signed payment */\n payload: {\n /** Signed transaction (base64 for Solana, JSON for EVM) */\n transaction: string;\n };\n}\n\n// ============================================================================\n// Facilitator Response Types\n// ============================================================================\n\n/**\n * Response from /verify endpoint\n */\nexport interface VerifyResponse {\n /** Whether the payment is valid */\n isValid: boolean;\n /** Reason for invalidity (if invalid) */\n invalidReason?: string;\n /** Payer address */\n payer?: string;\n}\n\n/**\n * Response from /settle endpoint\n */\nexport interface SettleResponse {\n /** Whether settlement succeeded */\n success: boolean;\n /** Transaction signature/hash */\n transaction?: string;\n /** Network the payment was made on */\n network: string;\n /** Error reason (if failed) */\n errorReason?: string;\n /** Error code (if failed) */\n errorCode?: string;\n /** Payer address */\n payer?: string;\n}\n\n// ============================================================================\n// Access Pass Types\n// ============================================================================\n\n/**\n * A single access pass tier offered by a seller\n */\nexport interface AccessPassTier {\n /** Tier ID (e.g., '1h', '24h') */\n id: string;\n /** Human-readable label (e.g., '1 hour') */\n label: string;\n /** Duration in seconds */\n seconds: number;\n /** Price in USD (e.g., '0.50') */\n price: string;\n /** Price in atomic units (e.g., '500000') */\n priceAtomic: string;\n}\n\n/**\n * Access pass info returned in X-ACCESS-PASS-TIERS header\n */\nexport interface AccessPassInfo {\n /** Available tiers (if tier-based pricing) */\n tiers?: AccessPassTier[];\n /** Rate per hour in USD (if custom duration pricing) */\n ratePerHour?: string;\n /** Pass issuer identifier */\n issuer?: string;\n}\n\n/**\n * JWT claims inside an access pass token\n */\nexport interface AccessPassClaims {\n /** Subject — always 'x402-access-pass' */\n sub: string;\n /** Tier ID or 'custom' */\n tier: string;\n /** Duration in seconds */\n duration: number;\n /** Issued at (unix seconds) */\n iat: number;\n /** Expires at (unix seconds) */\n exp: number;\n /** Payer wallet address */\n payer: string;\n /** Network used for payment */\n network: string;\n /** Issuer identifier */\n iss: string;\n}\n\n/**\n * Client-side access pass configuration\n */\nexport interface AccessPassClientConfig {\n /** Enable access pass mode (default: true when this config is present) */\n enabled?: boolean;\n /** Preferred tier ID (e.g., '1h') — pick this tier if available */\n preferTier?: string;\n /** Preferred custom duration in seconds (e.g., 3600) */\n preferDuration?: number;\n /** Maximum amount willing to spend in USD (e.g., '2.00') */\n maxSpend?: string;\n /** Auto-renew expired passes (default: true) */\n autoRenew?: boolean;\n}\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\n/**\n * SDK error codes\n */\nexport type X402ErrorCode =\n // Client errors\n | 'missing_payment_required_header'\n | 'invalid_payment_required'\n | 'unsupported_network'\n | 'no_matching_payment_option'\n | 'no_solana_accept' // Legacy, kept for compatibility\n | 'missing_fee_payer'\n | 'missing_decimals'\n | 'missing_amount'\n | 'amount_exceeds_max'\n | 'insufficient_balance'\n | 'wallet_missing_sign_transaction'\n | 'wallet_not_connected'\n | 'transaction_build_failed'\n | 'payment_rejected'\n // Server errors\n | 'invalid_payment_signature'\n | 'facilitator_verify_failed'\n | 'facilitator_settle_failed'\n | 'facilitator_request_failed'\n | 'no_matching_requirement'\n // Access pass errors\n | 'access_pass_expired'\n | 'access_pass_invalid'\n | 'access_pass_tier_not_found'\n | 'access_pass_exceeds_max_spend';\n\n/**\n * Custom error class for x402 operations\n */\nexport class X402Error extends Error {\n /** Error code for programmatic handling */\n code: X402ErrorCode;\n /** Additional error details */\n details?: unknown;\n\n constructor(code: X402ErrorCode, message: string, details?: unknown) {\n super(message);\n this.name = 'X402Error';\n this.code = code;\n this.details = details;\n // Maintain proper prototype chain\n Object.setPrototypeOf(this, X402Error.prototype);\n }\n}\n","/**\n * Utility Functions\n *\n * Chain-agnostic helpers for x402 payments.\n */\n\nimport { SOLANA_MAINNET_NETWORK, BASE_MAINNET_NETWORK } from './types';\n\n// ============================================================================\n// Amount Conversion\n// ============================================================================\n\n/**\n * Convert human-readable amount to atomic units\n *\n * @param amount - Human-readable amount (e.g., 0.05 for $0.05)\n * @param decimals - Token decimals (e.g., 6 for USDC)\n * @returns Amount in atomic units as string\n *\n * @example\n * ```typescript\n * toAtomicUnits(0.05, 6) // '50000'\n * toAtomicUnits(1.50, 6) // '1500000'\n * ```\n */\nexport function toAtomicUnits(amount: number, decimals: number): string {\n const multiplier = Math.pow(10, decimals);\n return Math.floor(amount * multiplier).toString();\n}\n\n/**\n * Convert atomic units to human-readable amount\n *\n * @param atomicUnits - Amount in smallest units\n * @param decimals - Token decimals\n * @returns Human-readable amount\n *\n * @example\n * ```typescript\n * fromAtomicUnits('50000', 6) // 0.05\n * fromAtomicUnits(1500000n, 6) // 1.5\n * ```\n */\nexport function fromAtomicUnits(\n atomicUnits: string | bigint | number,\n decimals: number\n): number {\n const divisor = Math.pow(10, decimals);\n return Number(atomicUnits) / divisor;\n}\n\n// ============================================================================\n// Network Helpers\n// ============================================================================\n\n/**\n * Network type\n */\nexport type ChainFamily = 'solana' | 'evm' | 'unknown';\n\n/**\n * Get the chain family from a CAIP-2 network identifier\n *\n * @param network - CAIP-2 network identifier\n * @returns Chain family\n *\n * @example\n * ```typescript\n * getChainFamily('solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp') // 'solana'\n * getChainFamily('eip155:8453') // 'evm'\n * ```\n */\nexport function getChainFamily(network: string): ChainFamily {\n if (network.startsWith('solana:') || network === 'solana') {\n return 'solana';\n }\n if (network.startsWith('eip155:') || ['base', 'ethereum', 'arbitrum'].includes(network)) {\n return 'evm';\n }\n return 'unknown';\n}\n\n/**\n * Get default RPC URL for a network\n *\n * @param network - CAIP-2 network identifier\n * @returns Default RPC URL\n */\nexport function getDefaultRpcUrl(network: string): string {\n const family = getChainFamily(network);\n\n if (family === 'solana') {\n if (network.includes('devnet') || network === 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1') {\n return 'https://api.devnet.solana.com';\n }\n if (network.includes('testnet') || network === 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z') {\n return 'https://api.testnet.solana.com';\n }\n // Mainnet uses Dexter's RPC proxy\n return 'https://api.dexter.cash/api/solana/rpc';\n }\n\n if (family === 'evm') {\n // Extract chain ID from CAIP-2\n if (network.startsWith('eip155:')) {\n const chainId = network.split(':')[1];\n switch (chainId) {\n case '8453': return 'https://api.dexter.cash/api/base/rpc'; // Dexter proxy\n case '84532': return 'https://sepolia.base.org';\n case '1': return 'https://eth.llamarpc.com';\n case '42161': return 'https://arb1.arbitrum.io/rpc';\n default: return 'https://api.dexter.cash/api/base/rpc';\n }\n }\n // Legacy names\n if (network === 'base') return 'https://api.dexter.cash/api/base/rpc';\n if (network === 'ethereum') return 'https://eth.llamarpc.com';\n if (network === 'arbitrum') return 'https://arb1.arbitrum.io/rpc';\n return 'https://api.dexter.cash/api/base/rpc';\n }\n\n // Unknown - return Dexter's Solana proxy\n return 'https://api.dexter.cash/api/solana/rpc';\n}\n\n/**\n * Get human-readable chain name\n *\n * @param network - CAIP-2 network identifier\n * @returns Human-readable name\n */\nexport function getChainName(network: string): string {\n const mapping: Record<string, string> = {\n [SOLANA_MAINNET_NETWORK]: 'Solana',\n 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1': 'Solana Devnet',\n 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z': 'Solana Testnet',\n 'solana': 'Solana',\n [BASE_MAINNET_NETWORK]: 'Base',\n 'eip155:84532': 'Base Sepolia',\n 'eip155:1': 'Ethereum',\n 'eip155:42161': 'Arbitrum One',\n 'base': 'Base',\n 'ethereum': 'Ethereum',\n 'arbitrum': 'Arbitrum',\n };\n return mapping[network] || network;\n}\n\n// ============================================================================\n// Transaction URL Helpers\n// ============================================================================\n\n/**\n * Get explorer URL for a transaction\n *\n * @param txSignature - Transaction signature/hash\n * @param network - CAIP-2 network identifier\n * @returns Explorer URL\n */\nexport function getExplorerUrl(txSignature: string, network: string): string {\n const family = getChainFamily(network);\n\n if (family === 'solana') {\n const isDevnet = network.includes('devnet') || network === 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1';\n if (isDevnet) {\n return `https://solscan.io/tx/${txSignature}?cluster=devnet`;\n }\n // Prefer Orb Markets for mainnet\n return `https://www.orbmarkets.io/tx/${txSignature}`;\n }\n\n if (family === 'evm') {\n // Extract chain ID\n let chainId = '8453'; // Default to Base\n if (network.startsWith('eip155:')) {\n chainId = network.split(':')[1];\n } else if (network === 'ethereum') {\n chainId = '1';\n } else if (network === 'arbitrum') {\n chainId = '42161';\n }\n\n switch (chainId) {\n case '8453': return `https://basescan.org/tx/${txSignature}`;\n case '84532': return `https://sepolia.basescan.org/tx/${txSignature}`;\n case '1': return `https://etherscan.io/tx/${txSignature}`;\n case '42161': return `https://arbiscan.io/tx/${txSignature}`;\n default: return `https://basescan.org/tx/${txSignature}`;\n }\n }\n\n return `https://solscan.io/tx/${txSignature}`;\n}\n\n// ============================================================================\n// Encoding Helpers\n// ============================================================================\n\n/**\n * Unicode-safe base64 encode a string.\n * Works in both Node.js and browsers, handling characters above U+00FF\n * that would cause btoa() to throw InvalidCharacterError.\n */\nfunction safeBase64Encode(str: string): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(str, 'utf-8').toString('base64');\n }\n // Browser fallback: encode UTF-8 bytes via TextEncoder\n const bytes = new TextEncoder().encode(str);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n\n/**\n * Unicode-safe base64 decode a string.\n * Works in both Node.js and browsers.\n */\nfunction safeBase64Decode(encoded: string): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(encoded, 'base64').toString('utf-8');\n }\n // Browser fallback: decode via atob then UTF-8\n const binary = atob(encoded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return new TextDecoder().decode(bytes);\n}\n\n/**\n * Encode an object as base64 JSON (Unicode-safe)\n */\nexport function encodeBase64Json(obj: unknown): string {\n return safeBase64Encode(JSON.stringify(obj));\n}\n\n/**\n * Decode base64 JSON to object (Unicode-safe)\n */\nexport function decodeBase64Json<T>(encoded: string): T {\n return JSON.parse(safeBase64Decode(encoded)) as T;\n}\n"],"mappings":";AAYO,IAAM,yBAAyB;AAG/B,IAAM,uBAAuB;;;ACU7B,SAAS,cAAc,QAAgB,UAA0B;AACtE,QAAM,aAAa,KAAK,IAAI,IAAI,QAAQ;AACxC,SAAO,KAAK,MAAM,SAAS,UAAU,EAAE,SAAS;AAClD;AAeO,SAAS,gBACd,aACA,UACQ;AACR,QAAM,UAAU,KAAK,IAAI,IAAI,QAAQ;AACrC,SAAO,OAAO,WAAW,IAAI;AAC/B;AAuBO,SAAS,eAAe,SAA8B;AAC3D,MAAI,QAAQ,WAAW,SAAS,KAAK,YAAY,UAAU;AACzD,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,WAAW,SAAS,KAAK,CAAC,QAAQ,YAAY,UAAU,EAAE,SAAS,OAAO,GAAG;AACvF,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAmDO,SAAS,aAAa,SAAyB;AACpD,QAAM,UAAkC;AAAA,IACtC,CAAC,sBAAsB,GAAG;AAAA,IAC1B,2CAA2C;AAAA,IAC3C,2CAA2C;AAAA,IAC3C,UAAU;AAAA,IACV,CAAC,oBAAoB,GAAG;AAAA,IACxB,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AACA,SAAO,QAAQ,OAAO,KAAK;AAC7B;AAaO,SAAS,eAAe,aAAqB,SAAyB;AAC3E,QAAM,SAAS,eAAe,OAAO;AAErC,MAAI,WAAW,UAAU;AACvB,UAAM,WAAW,QAAQ,SAAS,QAAQ,KAAK,YAAY;AAC3D,QAAI,UAAU;AACZ,aAAO,yBAAyB,WAAW;AAAA,IAC7C;AAEA,WAAO,gCAAgC,WAAW;AAAA,EACpD;AAEA,MAAI,WAAW,OAAO;AAEpB,QAAI,UAAU;AACd,QAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,gBAAU,QAAQ,MAAM,GAAG,EAAE,CAAC;AAAA,IAChC,WAAW,YAAY,YAAY;AACjC,gBAAU;AAAA,IACZ,WAAW,YAAY,YAAY;AACjC,gBAAU;AAAA,IACZ;AAEA,YAAQ,SAAS;AAAA,MACf,KAAK;AAAQ,eAAO,2BAA2B,WAAW;AAAA,MAC1D,KAAK;AAAS,eAAO,mCAAmC,WAAW;AAAA,MACnE,KAAK;AAAK,eAAO,2BAA2B,WAAW;AAAA,MACvD,KAAK;AAAS,eAAO,0BAA0B,WAAW;AAAA,MAC1D;AAAS,eAAO,2BAA2B,WAAW;AAAA,IACxD;AAAA,EACF;AAEA,SAAO,yBAAyB,WAAW;AAC7C;","names":[]}
|