| 
									
										
										
										
											2025-07-27 21:23:20 +00:00
										 |  |  | import * as plugins from './plugins.js'; | 
					
						
							|  |  |  | import * as types from './types.js'; | 
					
						
							| 
									
										
										
										
											2025-07-28 15:12:11 +00:00
										 |  |  | import { CoreResponse as AbstractCoreResponse } from '../core_base/response.js'; | 
					
						
							| 
									
										
										
										
											2025-07-27 21:23:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2025-07-28 15:12:11 +00:00
										 |  |  |  * Node.js implementation of Core Response class that provides a fetch-like API | 
					
						
							| 
									
										
										
										
											2025-07-27 21:23:20 +00:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | export class CoreResponse<T = any> | 
					
						
							|  |  |  |   extends AbstractCoreResponse<T> | 
					
						
							|  |  |  |   implements types.INodeResponse<T> | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2025-07-27 21:23:20 +00:00
										 |  |  |   private incomingMessage: plugins.http.IncomingMessage; | 
					
						
							|  |  |  |   private bodyBufferPromise: Promise<Buffer> | null = null; | 
					
						
							| 
									
										
										
										
											2025-07-29 15:44:04 +00:00
										 |  |  |   private _autoDrainTimeout: NodeJS.Immediate | null = null; | 
					
						
							| 
									
										
										
										
											2025-07-27 21:23:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Public properties
 | 
					
						
							|  |  |  |   public readonly ok: boolean; | 
					
						
							|  |  |  |   public readonly status: number; | 
					
						
							|  |  |  |   public readonly statusText: string; | 
					
						
							|  |  |  |   public readonly headers: plugins.http.IncomingHttpHeaders; | 
					
						
							|  |  |  |   public readonly url: string; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |   constructor( | 
					
						
							|  |  |  |     incomingMessage: plugins.http.IncomingMessage, | 
					
						
							|  |  |  |     url: string, | 
					
						
							|  |  |  |     options: types.ICoreRequestOptions = {}, | 
					
						
							|  |  |  |   ) { | 
					
						
							| 
									
										
										
										
											2025-07-28 15:12:11 +00:00
										 |  |  |     super(); | 
					
						
							| 
									
										
										
										
											2025-07-27 21:23:20 +00:00
										 |  |  |     this.incomingMessage = incomingMessage; | 
					
						
							|  |  |  |     this.url = url; | 
					
						
							|  |  |  |     this.status = incomingMessage.statusCode || 0; | 
					
						
							|  |  |  |     this.statusText = incomingMessage.statusMessage || ''; | 
					
						
							|  |  |  |     this.ok = this.status >= 200 && this.status < 300; | 
					
						
							|  |  |  |     this.headers = incomingMessage.headers; | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-29 15:44:04 +00:00
										 |  |  |     // Auto-drain unconsumed streams to prevent socket hanging
 | 
					
						
							|  |  |  |     // This prevents keep-alive sockets from timing out when response bodies aren't consumed
 | 
					
						
							|  |  |  |     // Default to true if not specified
 | 
					
						
							|  |  |  |     if (options.autoDrain !== false) { | 
					
						
							|  |  |  |       this._autoDrainTimeout = setImmediate(() => { | 
					
						
							|  |  |  |         if (!this.consumed && !this.incomingMessage.readableEnded) { | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |           console.log( | 
					
						
							|  |  |  |             `Auto-draining unconsumed response body for ${this.url} (status: ${this.status})`, | 
					
						
							|  |  |  |           ); | 
					
						
							| 
									
										
										
										
											2025-07-29 15:44:04 +00:00
										 |  |  |           this.incomingMessage.resume(); // Drain without processing
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Override to also cancel auto-drain when body is consumed | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   protected ensureNotConsumed(): void { | 
					
						
							|  |  |  |     // Cancel auto-drain since we're consuming the body
 | 
					
						
							|  |  |  |     if (this._autoDrainTimeout) { | 
					
						
							|  |  |  |       clearImmediate(this._autoDrainTimeout); | 
					
						
							|  |  |  |       this._autoDrainTimeout = null; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-29 15:44:04 +00:00
										 |  |  |     super.ensureNotConsumed(); | 
					
						
							| 
									
										
										
										
											2025-07-27 21:23:20 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Collects the body as a buffer | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   private async collectBody(): Promise<Buffer> { | 
					
						
							|  |  |  |     this.ensureNotConsumed(); | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-27 21:23:20 +00:00
										 |  |  |     if (this.bodyBufferPromise) { | 
					
						
							|  |  |  |       return this.bodyBufferPromise; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this.bodyBufferPromise = new Promise<Buffer>((resolve, reject) => { | 
					
						
							|  |  |  |       const chunks: Buffer[] = []; | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-27 21:23:20 +00:00
										 |  |  |       this.incomingMessage.on('data', (chunk: Buffer) => { | 
					
						
							|  |  |  |         chunks.push(chunk); | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-27 21:23:20 +00:00
										 |  |  |       this.incomingMessage.on('end', () => { | 
					
						
							|  |  |  |         resolve(Buffer.concat(chunks)); | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-27 21:23:20 +00:00
										 |  |  |       this.incomingMessage.on('error', reject); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return this.bodyBufferPromise; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Parse response as JSON | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   async json(): Promise<T> { | 
					
						
							|  |  |  |     const buffer = await this.collectBody(); | 
					
						
							|  |  |  |     const text = buffer.toString('utf-8'); | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-27 21:23:20 +00:00
										 |  |  |     try { | 
					
						
							|  |  |  |       return JSON.parse(text); | 
					
						
							|  |  |  |     } catch (error) { | 
					
						
							|  |  |  |       throw new Error(`Failed to parse JSON: ${error.message}`); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Get response as text | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   async text(): Promise<string> { | 
					
						
							|  |  |  |     const buffer = await this.collectBody(); | 
					
						
							|  |  |  |     return buffer.toString('utf-8'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Get response as ArrayBuffer | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   async arrayBuffer(): Promise<ArrayBuffer> { | 
					
						
							|  |  |  |     const buffer = await this.collectBody(); | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |     return buffer.buffer.slice( | 
					
						
							|  |  |  |       buffer.byteOffset, | 
					
						
							|  |  |  |       buffer.byteOffset + buffer.byteLength, | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2025-07-27 21:23:20 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							| 
									
										
										
										
											2025-07-28 22:50:12 +00:00
										 |  |  |    * Get response as a web-style ReadableStream | 
					
						
							| 
									
										
										
										
											2025-07-27 21:23:20 +00:00
										 |  |  |    */ | 
					
						
							| 
									
										
										
										
											2025-07-28 22:50:12 +00:00
										 |  |  |   stream(): ReadableStream<Uint8Array> | null { | 
					
						
							|  |  |  |     this.ensureNotConsumed(); | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 22:50:12 +00:00
										 |  |  |     // Convert Node.js stream to web stream
 | 
					
						
							|  |  |  |     // In Node.js 16.5+ we can use Readable.toWeb()
 | 
					
						
							|  |  |  |     if (this.incomingMessage.readableEnded || this.incomingMessage.destroyed) { | 
					
						
							|  |  |  |       return null; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 22:50:12 +00:00
										 |  |  |     // Create a web ReadableStream from the Node.js stream
 | 
					
						
							|  |  |  |     const nodeStream = this.incomingMessage; | 
					
						
							|  |  |  |     return new ReadableStream<Uint8Array>({ | 
					
						
							|  |  |  |       start(controller) { | 
					
						
							|  |  |  |         nodeStream.on('data', (chunk) => { | 
					
						
							|  |  |  |           controller.enqueue(new Uint8Array(chunk)); | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 22:50:12 +00:00
										 |  |  |         nodeStream.on('end', () => { | 
					
						
							|  |  |  |           controller.close(); | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 22:50:12 +00:00
										 |  |  |         nodeStream.on('error', (err) => { | 
					
						
							|  |  |  |           controller.error(err); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 22:50:12 +00:00
										 |  |  |       cancel() { | 
					
						
							|  |  |  |         nodeStream.destroy(); | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |       }, | 
					
						
							| 
									
										
										
										
											2025-07-28 22:50:12 +00:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 22:50:12 +00:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Get response as a Node.js readable stream | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   streamNode(): NodeJS.ReadableStream { | 
					
						
							| 
									
										
										
										
											2025-07-27 21:23:20 +00:00
										 |  |  |     this.ensureNotConsumed(); | 
					
						
							|  |  |  |     return this.incomingMessage; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Get the raw IncomingMessage (for legacy compatibility) | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   raw(): plugins.http.IncomingMessage { | 
					
						
							|  |  |  |     return this.incomingMessage; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | } |