11import type {
2- ApiResponse ,
32 AddressBalance ,
43 AddressUtxos ,
54 TransactionSubmission ,
65 NetworkInfo ,
76 FeeRecommendation ,
87 BrowserClientConfig ,
9- RequestOptions ,
108} from './client-web.types' ;
119import type { Transaction } from '@models/transaction.types' ;
10+ import type { ApiProvider } from './providers/api-provider.interface' ;
11+ import { HoosatProxyProvider } from './providers/hoosat-proxy-provider' ;
12+ import type { MultiProvider } from './providers/multi-provider' ;
1213
1314/**
1415 * HoosatWebClient - REST API client for browser-based Hoosat applications
1516 *
16- * Provides methods to interact with Hoosat blockchain via REST API proxy .
17+ * Now supports multiple API providers with automatic fallback and extensible architecture .
1718 * All methods return promises and handle errors gracefully.
1819 *
1920 * @example
2021 * ```typescript
22+ * // Using single provider (backward compatible)
2123 * const client = new HoosatWebClient({
2224 * baseUrl: 'https://proxy.hoosat.net/api/v1',
2325 * timeout: 30000
2426 * });
2527 *
26- * // Get balance
27- * const balance = await client.getBalance('hoosat:qz7ulu...' );
28- * console.log(`Balance: ${balance.balance} sompi` );
28+ * // Using custom provider
29+ * const customProvider = new HoosatProxyProvider({ baseUrl: 'https://proxy.hoosat.net/api/v1' } );
30+ * const client = new HoosatWebClient({ provider: customProvider } );
2931 *
30- * // Get UTXOs for transaction
31- * const utxos = await client.getUtxos('hoosat:qz7ulu...');
32+ * // Using multiple providers with fallback
33+ * const multiProvider = new MultiProvider({
34+ * providers: [proxyProvider, networkProvider],
35+ * strategy: 'failover'
36+ * });
37+ * const client = new HoosatWebClient({ provider: multiProvider });
3238 * ```
3339 */
3440export class HoosatWebClient {
35- private readonly _baseUrl : string ;
36- private readonly _timeout : number ;
37- private readonly _headers : Record < string , string > ;
38- private readonly _debug : boolean ;
41+ private readonly provider : ApiProvider ;
3942
4043 /**
4144 * Creates a new HoosatWebClient instance
4245 *
4346 * @param config - Client configuration
44- * @param config.baseUrl - Base URL of the API (e.g., 'https://proxy.hoosat.net/api/v1')
47+ * @param config.baseUrl - Base URL of the API (backward compatibility)
48+ * @param config.provider - Custom API provider instance
4549 * @param config.timeout - Request timeout in milliseconds (default: 30000)
4650 * @param config.headers - Additional headers to include in requests
4751 * @param config.debug - Enable debug logging (default: false)
4852 */
49- constructor ( config : BrowserClientConfig ) {
50- this . _baseUrl = config . baseUrl . replace ( / \/ $ / , '' ) ; // Remove trailing slash
51- this . _timeout = config . timeout || 30000 ;
52- this . _headers = {
53- 'Content-Type' : 'application/json' ,
54- ...config . headers ,
55- } ;
56- this . _debug = config . debug || false ;
57- }
58-
59- // ==================== PRIVATE HELPERS ====================
60-
61- /**
62- * Make HTTP request with timeout and error handling
63- * @private
64- */
65- private async request < T > ( endpoint : string , options : RequestInit & RequestOptions = { } ) : Promise < T > {
66- const url = `${ this . _baseUrl } ${ endpoint } ` ;
67- const timeout = options . timeout || this . _timeout ;
68-
69- if ( this . _debug ) {
70- console . log ( `[HoosatWebClient] ${ options . method || 'GET' } ${ url } ` ) ;
71- if ( options . body ) {
72- console . log ( '[HoosatWebClient] Request body:' , options . body ) ;
73- }
74- }
75-
76- // Create abort controller for timeout
77- const controller = new AbortController ( ) ;
78- const timeoutId = setTimeout ( ( ) => controller . abort ( ) , timeout ) ;
79-
80- try {
81- const response = await fetch ( url , {
82- ...options ,
83- headers : {
84- ...this . _headers ,
85- ...options . headers ,
86- } ,
87- signal : controller . signal ,
53+ constructor ( config : BrowserClientConfig & { provider ?: ApiProvider } ) {
54+ if ( config . provider ) {
55+ this . provider = config . provider ;
56+ } else if ( config . baseUrl ) {
57+ this . provider = new HoosatProxyProvider ( {
58+ baseUrl : config . baseUrl ,
59+ timeout : config . timeout ,
60+ headers : config . headers ,
61+ debug : config . debug ,
8862 } ) ;
89-
90- clearTimeout ( timeoutId ) ;
91-
92- // Parse response
93- const data : ApiResponse < T > = ( await response . json ( ) ) as ApiResponse < T > ;
94-
95- if ( this . _debug ) {
96- console . log ( '[HoosatWebClient] Response:' , data ) ;
97- }
98-
99- // Check API response format
100- if ( ! data . success ) {
101- throw new Error ( data . error || 'API request failed' ) ;
102- }
103-
104- if ( ! data . data ) {
105- throw new Error ( 'API response missing data field' ) ;
106- }
107-
108- return data . data ;
109- } catch ( error : any ) {
110- clearTimeout ( timeoutId ) ;
111-
112- if ( error . name === 'AbortError' ) {
113- throw new Error ( `Request timeout after ${ timeout } ms` ) ;
114- }
115-
116- if ( this . _debug ) {
117- console . error ( '[HoosatWebClient] Error:' , error ) ;
118- }
119-
120- throw error ;
63+ } else {
64+ throw new Error ( 'Either baseUrl or provider must be specified' ) ;
12165 }
12266 }
12367
124- // ==================== ADDRESS METHODS ====================
68+ // ==================== PUBLIC API METHODS ====================
12569
12670 /**
12771 * Get balance for a Hoosat address
@@ -137,7 +81,7 @@ export class HoosatWebClient {
13781 * ```
13882 */
13983 async getBalance ( address : string ) : Promise < AddressBalance > {
140- return this . request < AddressBalance > ( `/ address/ ${ address } /balance` ) ;
84+ return this . provider . getBalance ( address ) ;
14185 }
14286
14387 /**
@@ -160,32 +104,9 @@ export class HoosatWebClient {
160104 * ```
161105 */
162106 async getUtxos ( addresses : string [ ] ) : Promise < AddressUtxos > {
163- const response = await this . request < any > ( '/address/utxos' , {
164- method : 'POST' ,
165- body : JSON . stringify ( { addresses } ) ,
166- } ) ;
167-
168- // Adapt API response to TxBuilder format
169- // API returns: scriptPublicKey.scriptPublicKey
170- // TxBuilder expects: scriptPublicKey.script
171- if ( response . utxos ) {
172- response . utxos = response . utxos . map ( ( utxo : any ) => ( {
173- ...utxo ,
174- utxoEntry : {
175- ...utxo . utxoEntry ,
176- scriptPublicKey : {
177- version : utxo . utxoEntry . scriptPublicKey . version ,
178- script : utxo . utxoEntry . scriptPublicKey . scriptPublicKey , // Rename field
179- } ,
180- } ,
181- } ) ) ;
182- }
183-
184- return response ;
107+ return this . provider . getUtxos ( addresses ) ;
185108 }
186109
187- // ==================== TRANSACTION METHODS ====================
188-
189110 /**
190111 * Submit a signed transaction to the network
191112 *
@@ -205,14 +126,9 @@ export class HoosatWebClient {
205126 * ```
206127 */
207128 async submitTransaction ( transaction : Transaction ) : Promise < TransactionSubmission > {
208- return this . request < TransactionSubmission > ( '/transaction/submit' , {
209- method : 'POST' ,
210- body : JSON . stringify ( transaction ) ,
211- } ) ;
129+ return this . provider . submitTransaction ( transaction ) ;
212130 }
213131
214- // ==================== NETWORK METHODS ====================
215-
216132 /**
217133 * Get network information
218134 *
@@ -227,7 +143,7 @@ export class HoosatWebClient {
227143 * ```
228144 */
229145 async getNetworkInfo ( ) : Promise < NetworkInfo > {
230- return this . request < NetworkInfo > ( '/node/info' ) ;
146+ return this . provider . getNetworkInfo ( ) ;
231147 }
232148
233149 /**
@@ -245,7 +161,7 @@ export class HoosatWebClient {
245161 * ```
246162 */
247163 async getFeeEstimate ( ) : Promise < FeeRecommendation > {
248- return this . request < FeeRecommendation > ( '/mempool/fee-estimate' ) ;
164+ return this . provider . getFeeEstimate ( ) ;
249165 }
250166
251167 // ==================== UTILITY METHODS ====================
@@ -264,25 +180,15 @@ export class HoosatWebClient {
264180 * ```
265181 */
266182 async ping ( ) : Promise < boolean > {
267- try {
268- await this . getNetworkInfo ( ) ;
269- return true ;
270- } catch ( error ) {
271- return false ;
272- }
183+ return this . provider . ping ( ) ;
273184 }
274185
275186 /**
276- * Get current configuration
187+ * Get the current API provider instance
277188 *
278- * @returns Client configuration
189+ * @returns Current provider
279190 */
280- getConfig ( ) : BrowserClientConfig {
281- return {
282- baseUrl : this . _baseUrl ,
283- timeout : this . _timeout ,
284- headers : { ...this . _headers } ,
285- debug : this . _debug ,
286- } ;
191+ getProvider ( ) : ApiProvider {
192+ return this . provider ;
287193 }
288194}
0 commit comments