| 
									
										
										
										
											2025-07-28 16:51:30 +00:00
										 |  |  | import * as types from './types.js'; | 
					
						
							|  |  |  | import { CoreResponse } from './response.js'; | 
					
						
							|  |  |  | import { CoreRequest as AbstractCoreRequest } from '../core_base/request.js'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Fetch-based implementation of Core Request class | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | export class CoreRequest extends AbstractCoreRequest< | 
					
						
							|  |  |  |   types.ICoreRequestOptions, | 
					
						
							|  |  |  |   CoreResponse | 
					
						
							|  |  |  | > { | 
					
						
							| 
									
										
										
										
											2025-08-19 01:36:44 +00:00
										 |  |  |   private timeoutId: ReturnType<typeof setTimeout> | null = null; | 
					
						
							|  |  |  |   private abortController: AbortController | null = null; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 16:51:30 +00:00
										 |  |  |   constructor(url: string, options: types.ICoreRequestOptions = {}) { | 
					
						
							|  |  |  |     super(url, options); | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 22:37:36 +00:00
										 |  |  |     // Check for unsupported Node.js-specific options
 | 
					
						
							|  |  |  |     if (options.agent || options.socketPath) { | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |       throw new Error( | 
					
						
							|  |  |  |         'Node.js specific options (agent, socketPath) are not supported in browser/fetch implementation', | 
					
						
							|  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2025-07-28 22:37:36 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-07-28 16:51:30 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Build the full URL with query parameters | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   private buildUrl(): string { | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |     if ( | 
					
						
							|  |  |  |       !this.options.queryParams || | 
					
						
							|  |  |  |       Object.keys(this.options.queryParams).length === 0 | 
					
						
							|  |  |  |     ) { | 
					
						
							| 
									
										
										
										
											2025-07-28 16:51:30 +00:00
										 |  |  |       return this.url; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const url = new URL(this.url); | 
					
						
							|  |  |  |     Object.entries(this.options.queryParams).forEach(([key, value]) => { | 
					
						
							|  |  |  |       url.searchParams.append(key, value); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     return url.toString(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Convert our options to fetch RequestInit | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   private buildFetchOptions(): RequestInit { | 
					
						
							|  |  |  |     const fetchOptions: RequestInit = { | 
					
						
							|  |  |  |       method: this.options.method, | 
					
						
							|  |  |  |       headers: this.options.headers, | 
					
						
							|  |  |  |       credentials: this.options.credentials, | 
					
						
							|  |  |  |       mode: this.options.mode, | 
					
						
							|  |  |  |       cache: this.options.cache, | 
					
						
							|  |  |  |       redirect: this.options.redirect, | 
					
						
							|  |  |  |       referrer: this.options.referrer, | 
					
						
							|  |  |  |       referrerPolicy: this.options.referrerPolicy, | 
					
						
							|  |  |  |       integrity: this.options.integrity, | 
					
						
							|  |  |  |       keepalive: this.options.keepAlive, | 
					
						
							|  |  |  |       signal: this.options.signal, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Handle request body
 | 
					
						
							|  |  |  |     if (this.options.requestBody !== undefined) { | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |       if ( | 
					
						
							|  |  |  |         typeof this.options.requestBody === 'string' || | 
					
						
							|  |  |  |         this.options.requestBody instanceof ArrayBuffer || | 
					
						
							| 
									
										
										
										
											2025-08-19 01:36:44 +00:00
										 |  |  |         this.options.requestBody instanceof Uint8Array || | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |         this.options.requestBody instanceof FormData || | 
					
						
							|  |  |  |         this.options.requestBody instanceof URLSearchParams || | 
					
						
							| 
									
										
										
										
											2025-08-19 01:36:44 +00:00
										 |  |  |         this.options.requestBody instanceof ReadableStream || | 
					
						
							|  |  |  |         // Check for Buffer (Node.js polyfills in browser may provide this)
 | 
					
						
							|  |  |  |         (typeof Buffer !== 'undefined' && this.options.requestBody instanceof Buffer) | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |       ) { | 
					
						
							| 
									
										
										
										
											2025-07-28 16:51:30 +00:00
										 |  |  |         fetchOptions.body = this.options.requestBody; | 
					
						
							| 
									
										
										
										
											2025-08-19 01:36:44 +00:00
										 |  |  |          | 
					
						
							|  |  |  |         // If streaming, we need to set duplex mode
 | 
					
						
							|  |  |  |         if (this.options.requestBody instanceof ReadableStream) { | 
					
						
							|  |  |  |           (fetchOptions as any).duplex = 'half'; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-07-28 16:51:30 +00:00
										 |  |  |       } else { | 
					
						
							|  |  |  |         // Convert objects to JSON
 | 
					
						
							|  |  |  |         fetchOptions.body = JSON.stringify(this.options.requestBody); | 
					
						
							|  |  |  |         // Set content-type if not already set
 | 
					
						
							|  |  |  |         if (!fetchOptions.headers) { | 
					
						
							|  |  |  |           fetchOptions.headers = { 'Content-Type': 'application/json' }; | 
					
						
							|  |  |  |         } else if (fetchOptions.headers instanceof Headers) { | 
					
						
							|  |  |  |           if (!fetchOptions.headers.has('Content-Type')) { | 
					
						
							|  |  |  |             fetchOptions.headers.set('Content-Type', 'application/json'); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |         } else if ( | 
					
						
							|  |  |  |           typeof fetchOptions.headers === 'object' && | 
					
						
							|  |  |  |           !Array.isArray(fetchOptions.headers) | 
					
						
							|  |  |  |         ) { | 
					
						
							| 
									
										
										
										
											2025-07-28 16:51:30 +00:00
										 |  |  |           const headersObj = fetchOptions.headers as Record<string, string>; | 
					
						
							|  |  |  |           if (!headersObj['Content-Type']) { | 
					
						
							|  |  |  |             headersObj['Content-Type'] = 'application/json'; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Handle timeout
 | 
					
						
							|  |  |  |     if (this.options.timeout || this.options.hardDataCuttingTimeout) { | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |       const timeout = | 
					
						
							|  |  |  |         this.options.hardDataCuttingTimeout || this.options.timeout; | 
					
						
							| 
									
										
										
										
											2025-08-19 01:36:44 +00:00
										 |  |  |       this.abortController = new AbortController(); | 
					
						
							|  |  |  |       this.timeoutId = setTimeout(() => { | 
					
						
							|  |  |  |         if (this.abortController) { | 
					
						
							|  |  |  |           this.abortController.abort(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, timeout); | 
					
						
							|  |  |  |       fetchOptions.signal = this.abortController.signal; | 
					
						
							| 
									
										
										
										
											2025-07-28 16:51:30 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return fetchOptions; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Fire the request and return a CoreResponse | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   async fire(): Promise<CoreResponse> { | 
					
						
							|  |  |  |     const response = await this.fireCore(); | 
					
						
							|  |  |  |     return new CoreResponse(response); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Fire the request and return the raw Response | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   async fireCore(): Promise<Response> { | 
					
						
							|  |  |  |     const url = this.buildUrl(); | 
					
						
							|  |  |  |     const options = this.buildFetchOptions(); | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 16:51:30 +00:00
										 |  |  |     try { | 
					
						
							|  |  |  |       const response = await fetch(url, options); | 
					
						
							| 
									
										
										
										
											2025-08-19 01:36:44 +00:00
										 |  |  |       // Clear timeout on successful response
 | 
					
						
							|  |  |  |       this.clearTimeout(); | 
					
						
							| 
									
										
										
										
											2025-07-28 16:51:30 +00:00
										 |  |  |       return response; | 
					
						
							|  |  |  |     } catch (error) { | 
					
						
							| 
									
										
										
										
											2025-08-19 01:36:44 +00:00
										 |  |  |       // Clear timeout on error
 | 
					
						
							|  |  |  |       this.clearTimeout(); | 
					
						
							| 
									
										
										
										
											2025-07-28 16:51:30 +00:00
										 |  |  |       if (error.name === 'AbortError') { | 
					
						
							|  |  |  |         throw new Error('Request timed out'); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       throw error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-19 01:36:44 +00:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Clear the timeout and abort controller | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   private clearTimeout(): void { | 
					
						
							|  |  |  |     if (this.timeoutId) { | 
					
						
							|  |  |  |       clearTimeout(this.timeoutId); | 
					
						
							|  |  |  |       this.timeoutId = null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (this.abortController) { | 
					
						
							|  |  |  |       this.abortController = null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 16:51:30 +00:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Static factory method to create and fire a request | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   static async create( | 
					
						
							|  |  |  |     url: string, | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |     options: types.ICoreRequestOptions = {}, | 
					
						
							| 
									
										
										
										
											2025-07-28 16:51:30 +00:00
										 |  |  |   ): Promise<CoreResponse> { | 
					
						
							|  |  |  |     const request = new CoreRequest(url, options); | 
					
						
							|  |  |  |     return request.fire(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Convenience exports for backward compatibility | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export const isUnixSocket = CoreRequest.isUnixSocket; | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | export const parseUnixSocketUrl = CoreRequest.parseUnixSocketUrl; |