| 
									
										
										
										
											2025-07-28 17:23:48 +00:00
										 |  |  | import { CoreRequest, CoreResponse } from '../core/index.js'; | 
					
						
							| 
									
										
										
										
											2025-07-28 22:37:36 +00:00
										 |  |  | import type { ICoreResponse } from '../core_base/types.js'; | 
					
						
							|  |  |  | import * as plugins from './plugins.js'; | 
					
						
							|  |  |  | import type { ICoreRequestOptions } from '../core_base/types.js'; | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | import type { | 
					
						
							|  |  |  |   HttpMethod, | 
					
						
							|  |  |  |   ResponseType, | 
					
						
							|  |  |  |   FormField, | 
					
						
							|  |  |  |   RateLimitConfig, | 
					
						
							| 
									
										
										
										
											2025-08-18 22:29:24 +00:00
										 |  |  |   RawStreamFunction, | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | } from './types/common.js'; | 
					
						
							| 
									
										
										
										
											2025-04-03 06:52:58 +00:00
										 |  |  | import { | 
					
						
							|  |  |  |   type TPaginationConfig, | 
					
						
							|  |  |  |   PaginationStrategy, | 
					
						
							|  |  |  |   type OffsetPaginationConfig, | 
					
						
							|  |  |  |   type CursorPaginationConfig, | 
					
						
							|  |  |  |   type CustomPaginationConfig, | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |   type TPaginatedResponse, | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  | } from './types/pagination.js'; | 
					
						
							|  |  |  | import { createPaginatedResponse } from './features/pagination.js'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-29 13:19:43 +00:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Parse Retry-After header value to milliseconds | 
					
						
							|  |  |  |  * @param retryAfter - The Retry-After header value (seconds or HTTP date) | 
					
						
							|  |  |  |  * @returns Delay in milliseconds | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function parseRetryAfter(retryAfter: string | string[]): number { | 
					
						
							|  |  |  |   // Handle array of values (take first)
 | 
					
						
							|  |  |  |   const value = Array.isArray(retryAfter) ? retryAfter[0] : retryAfter; | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-29 13:19:43 +00:00
										 |  |  |   if (!value) return 0; | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-29 13:19:43 +00:00
										 |  |  |   // Try to parse as seconds (number)
 | 
					
						
							|  |  |  |   const seconds = parseInt(value, 10); | 
					
						
							|  |  |  |   if (!isNaN(seconds)) { | 
					
						
							|  |  |  |     return seconds * 1000; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-29 13:19:43 +00:00
										 |  |  |   // Try to parse as HTTP date
 | 
					
						
							|  |  |  |   const retryDate = new Date(value); | 
					
						
							|  |  |  |   if (!isNaN(retryDate.getTime())) { | 
					
						
							|  |  |  |     return Math.max(0, retryDate.getTime() - Date.now()); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-29 13:19:43 +00:00
										 |  |  |   return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Modern fluent client for making HTTP requests | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2025-07-28 23:20:52 +00:00
										 |  |  | export class SmartRequest<T = any> { | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |   private _url: string; | 
					
						
							| 
									
										
										
										
											2025-07-28 22:37:36 +00:00
										 |  |  |   private _options: ICoreRequestOptions = {}; | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |   private _retries: number = 0; | 
					
						
							|  |  |  |   private _queryParams: Record<string, string> = {}; | 
					
						
							|  |  |  |   private _paginationConfig?: TPaginationConfig; | 
					
						
							| 
									
										
										
										
											2025-07-29 13:19:43 +00:00
										 |  |  |   private _rateLimitConfig?: RateLimitConfig; | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							| 
									
										
										
										
											2025-07-28 23:20:52 +00:00
										 |  |  |    * Create a new SmartRequest instance | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |    */ | 
					
						
							| 
									
										
										
										
											2025-07-28 23:20:52 +00:00
										 |  |  |   static create<T = any>(): SmartRequest<T> { | 
					
						
							|  |  |  |     return new SmartRequest<T>(); | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Set the URL for the request | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   url(url: string): this { | 
					
						
							|  |  |  |     this._url = url; | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Set the HTTP method | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   method(method: HttpMethod): this { | 
					
						
							|  |  |  |     this._options.method = method; | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Set JSON body for the request | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   json(data: any): this { | 
					
						
							|  |  |  |     if (!this._options.headers) { | 
					
						
							|  |  |  |       this._options.headers = {}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     this._options.headers['Content-Type'] = 'application/json'; | 
					
						
							|  |  |  |     this._options.requestBody = data; | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Set form data for the request | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   formData(data: FormField[]): this { | 
					
						
							|  |  |  |     const form = new plugins.formData(); | 
					
						
							| 
									
										
										
										
											2025-04-03 06:52:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     for (const item of data) { | 
					
						
							|  |  |  |       if (Buffer.isBuffer(item.value)) { | 
					
						
							|  |  |  |         form.append(item.name, item.value, { | 
					
						
							|  |  |  |           filename: item.filename || 'file', | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |           contentType: item.contentType || 'application/octet-stream', | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |         }); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         form.append(item.name, item.value); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-04-03 06:52:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     if (!this._options.headers) { | 
					
						
							|  |  |  |       this._options.headers = {}; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-04-03 06:52:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     this._options.headers = { | 
					
						
							|  |  |  |       ...this._options.headers, | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |       ...form.getHeaders(), | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2025-04-03 06:52:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     this._options.requestBody = form; | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-18 22:29:24 +00:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Set raw buffer data for the request | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   buffer(data: Buffer | Uint8Array, contentType?: string): this { | 
					
						
							|  |  |  |     if (!this._options.headers) { | 
					
						
							|  |  |  |       this._options.headers = {}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     this._options.headers['Content-Type'] = contentType || 'application/octet-stream'; | 
					
						
							|  |  |  |     this._options.requestBody = data; | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Stream data for the request | 
					
						
							|  |  |  |    * Accepts Node.js Readable streams or web ReadableStream | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   stream(stream: NodeJS.ReadableStream | ReadableStream<Uint8Array>, contentType?: string): this { | 
					
						
							|  |  |  |     if (!this._options.headers) { | 
					
						
							|  |  |  |       this._options.headers = {}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // Set content type if provided
 | 
					
						
							|  |  |  |     if (contentType) { | 
					
						
							|  |  |  |       this._options.headers['Content-Type'] = contentType; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // Check if it's a Node.js stream (has pipe method)
 | 
					
						
							|  |  |  |     if ('pipe' in stream && typeof (stream as any).pipe === 'function') { | 
					
						
							|  |  |  |       // For Node.js streams, we need to use a custom approach
 | 
					
						
							|  |  |  |       // Store the stream to be used later
 | 
					
						
							|  |  |  |       (this._options as any).__nodeStream = stream; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       // For web ReadableStream, pass directly
 | 
					
						
							|  |  |  |       this._options.requestBody = stream; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Provide a custom function to handle raw request streaming | 
					
						
							|  |  |  |    * This gives full control over the request body streaming | 
					
						
							| 
									
										
										
										
											2025-08-19 01:36:44 +00:00
										 |  |  |    * Note: Only works in Node.js environment, not supported in browsers | 
					
						
							| 
									
										
										
										
											2025-08-18 22:29:24 +00:00
										 |  |  |    */ | 
					
						
							|  |  |  |   raw(streamFunc: RawStreamFunction): this { | 
					
						
							|  |  |  |     // Store the raw streaming function to be used later
 | 
					
						
							|  |  |  |     (this._options as any).__rawStreamFunc = streamFunc; | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Set request timeout in milliseconds | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   timeout(ms: number): this { | 
					
						
							|  |  |  |     this._options.timeout = ms; | 
					
						
							|  |  |  |     this._options.hardDataCuttingTimeout = ms; | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Set number of retry attempts | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   retry(count: number): this { | 
					
						
							|  |  |  |     this._retries = count; | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-29 13:19:43 +00:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Enable automatic 429 (Too Many Requests) handling with configurable backoff | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   handle429Backoff(config?: RateLimitConfig): this { | 
					
						
							|  |  |  |     this._rateLimitConfig = { | 
					
						
							|  |  |  |       maxRetries: config?.maxRetries ?? 3, | 
					
						
							|  |  |  |       respectRetryAfter: config?.respectRetryAfter ?? true, | 
					
						
							|  |  |  |       maxWaitTime: config?.maxWaitTime ?? 60000, | 
					
						
							|  |  |  |       fallbackDelay: config?.fallbackDelay ?? 1000, | 
					
						
							|  |  |  |       backoffFactor: config?.backoffFactor ?? 2, | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |       onRateLimit: config?.onRateLimit, | 
					
						
							| 
									
										
										
										
											2025-07-29 13:19:43 +00:00
										 |  |  |     }; | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Set HTTP headers | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   headers(headers: Record<string, string>): this { | 
					
						
							|  |  |  |     if (!this._options.headers) { | 
					
						
							|  |  |  |       this._options.headers = {}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     this._options.headers = { | 
					
						
							|  |  |  |       ...this._options.headers, | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |       ...headers, | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     }; | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Set a single HTTP header | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   header(name: string, value: string): this { | 
					
						
							|  |  |  |     if (!this._options.headers) { | 
					
						
							|  |  |  |       this._options.headers = {}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     this._options.headers[name] = value; | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Set query parameters | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   query(params: Record<string, string>): this { | 
					
						
							|  |  |  |     this._queryParams = { | 
					
						
							|  |  |  |       ...this._queryParams, | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |       ...params, | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     }; | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-29 13:19:43 +00:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Set additional request options | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   options(options: Partial<ICoreRequestOptions>): this { | 
					
						
							|  |  |  |     this._options = { | 
					
						
							|  |  |  |       ...this._options, | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |       ...options, | 
					
						
							| 
									
										
										
										
											2025-07-29 13:19:43 +00:00
										 |  |  |     }; | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-29 15:44:04 +00:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Enable or disable auto-drain for unconsumed response bodies (Node.js only) | 
					
						
							|  |  |  |    * Default is true to prevent socket hanging | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   autoDrain(enabled: boolean): this { | 
					
						
							|  |  |  |     this._options.autoDrain = enabled; | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |   /** | 
					
						
							| 
									
										
										
										
											2025-07-27 21:23:20 +00:00
										 |  |  |    * Set the Accept header to indicate what content type is expected | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |    */ | 
					
						
							| 
									
										
										
										
											2025-07-27 21:23:20 +00:00
										 |  |  |   accept(type: ResponseType): this { | 
					
						
							|  |  |  |     // Map response types to Accept header values
 | 
					
						
							|  |  |  |     const acceptHeaders: Record<ResponseType, string> = { | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |       json: 'application/json', | 
					
						
							|  |  |  |       text: 'text/plain', | 
					
						
							|  |  |  |       binary: 'application/octet-stream', | 
					
						
							|  |  |  |       stream: '*/*', | 
					
						
							| 
									
										
										
										
											2025-07-27 21:23:20 +00:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-27 21:23:20 +00:00
										 |  |  |     return this.header('Accept', acceptHeaders[type]); | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Configure pagination for requests | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   pagination(config: TPaginationConfig): this { | 
					
						
							|  |  |  |     this._paginationConfig = config; | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Configure offset-based pagination (page & limit) | 
					
						
							|  |  |  |    */ | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |   withOffsetPagination( | 
					
						
							|  |  |  |     config: Omit<OffsetPaginationConfig, 'strategy'> = {}, | 
					
						
							|  |  |  |   ): this { | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     this._paginationConfig = { | 
					
						
							|  |  |  |       strategy: PaginationStrategy.OFFSET, | 
					
						
							|  |  |  |       pageParam: config.pageParam || 'page', | 
					
						
							|  |  |  |       limitParam: config.limitParam || 'limit', | 
					
						
							|  |  |  |       startPage: config.startPage || 1, | 
					
						
							|  |  |  |       pageSize: config.pageSize || 20, | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |       totalPath: config.totalPath || 'total', | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2025-04-03 06:52:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     // Add initial pagination parameters
 | 
					
						
							|  |  |  |     this.query({ | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |       [this._paginationConfig.pageParam]: String( | 
					
						
							|  |  |  |         this._paginationConfig.startPage, | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       [this._paginationConfig.limitParam]: String( | 
					
						
							|  |  |  |         this._paginationConfig.pageSize, | 
					
						
							|  |  |  |       ), | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2025-04-03 06:52:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Configure cursor-based pagination | 
					
						
							|  |  |  |    */ | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |   withCursorPagination( | 
					
						
							|  |  |  |     config: Omit<CursorPaginationConfig, 'strategy'> = {}, | 
					
						
							|  |  |  |   ): this { | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     this._paginationConfig = { | 
					
						
							|  |  |  |       strategy: PaginationStrategy.CURSOR, | 
					
						
							|  |  |  |       cursorParam: config.cursorParam || 'cursor', | 
					
						
							|  |  |  |       cursorPath: config.cursorPath || 'nextCursor', | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |       hasMorePath: config.hasMorePath || 'hasMore', | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     }; | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Configure Link header-based pagination | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   withLinkPagination(): this { | 
					
						
							|  |  |  |     this._paginationConfig = { | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |       strategy: PaginationStrategy.LINK_HEADER, | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     }; | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Configure custom pagination | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   withCustomPagination(config: Omit<CustomPaginationConfig, 'strategy'>): this { | 
					
						
							|  |  |  |     this._paginationConfig = { | 
					
						
							|  |  |  |       strategy: PaginationStrategy.CUSTOM, | 
					
						
							|  |  |  |       hasNextPage: config.hasNextPage, | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |       getNextPageParams: config.getNextPageParams, | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     }; | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Make a GET request | 
					
						
							|  |  |  |    */ | 
					
						
							| 
									
										
										
										
											2025-07-28 22:37:36 +00:00
										 |  |  |   async get<R = T>(): Promise<ICoreResponse<R>> { | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     return this.execute<R>('GET'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Make a POST request | 
					
						
							|  |  |  |    */ | 
					
						
							| 
									
										
										
										
											2025-07-28 22:37:36 +00:00
										 |  |  |   async post<R = T>(): Promise<ICoreResponse<R>> { | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     return this.execute<R>('POST'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Make a PUT request | 
					
						
							|  |  |  |    */ | 
					
						
							| 
									
										
										
										
											2025-07-28 22:37:36 +00:00
										 |  |  |   async put<R = T>(): Promise<ICoreResponse<R>> { | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     return this.execute<R>('PUT'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Make a DELETE request | 
					
						
							|  |  |  |    */ | 
					
						
							| 
									
										
										
										
											2025-07-28 22:37:36 +00:00
										 |  |  |   async delete<R = T>(): Promise<ICoreResponse<R>> { | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     return this.execute<R>('DELETE'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Make a PATCH request | 
					
						
							|  |  |  |    */ | 
					
						
							| 
									
										
										
										
											2025-07-28 22:37:36 +00:00
										 |  |  |   async patch<R = T>(): Promise<ICoreResponse<R>> { | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     return this.execute<R>('PATCH'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Get paginated response | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   async getPaginated<ItemType = T>(): Promise<TPaginatedResponse<ItemType>> { | 
					
						
							|  |  |  |     if (!this._paginationConfig) { | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |       throw new Error( | 
					
						
							|  |  |  |         'Pagination not configured. Call one of the pagination methods first.', | 
					
						
							|  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-04-03 06:52:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     // Default to GET if no method specified
 | 
					
						
							|  |  |  |     if (!this._options.method) { | 
					
						
							|  |  |  |       this._options.method = 'GET'; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-04-03 06:52:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     const response = await this.execute(); | 
					
						
							| 
									
										
										
										
											2025-04-03 06:52:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-27 21:23:20 +00:00
										 |  |  |     return await createPaginatedResponse<ItemType>( | 
					
						
							| 
									
										
										
										
											2025-04-03 06:52:58 +00:00
										 |  |  |       response, | 
					
						
							|  |  |  |       this._paginationConfig, | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |       this._queryParams, | 
					
						
							|  |  |  |       (nextPageParams) => { | 
					
						
							|  |  |  |         // Create a new client with the same configuration but updated query params
 | 
					
						
							| 
									
										
										
										
											2025-07-28 23:20:52 +00:00
										 |  |  |         const nextClient = new SmartRequest<ItemType>(); | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |         Object.assign(nextClient, this); | 
					
						
							|  |  |  |         nextClient._queryParams = nextPageParams; | 
					
						
							| 
									
										
										
										
											2025-04-03 06:52:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |         return nextClient.getPaginated<ItemType>(); | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |       }, | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Get all pages at once (use with caution for large datasets) | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   async getAllPages<ItemType = T>(): Promise<ItemType[]> { | 
					
						
							|  |  |  |     const firstPage = await this.getPaginated<ItemType>(); | 
					
						
							|  |  |  |     return firstPage.getAllPages(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Execute the HTTP request | 
					
						
							|  |  |  |    */ | 
					
						
							| 
									
										
										
										
											2025-07-28 22:37:36 +00:00
										 |  |  |   private async execute<R = T>(method?: HttpMethod): Promise<ICoreResponse<R>> { | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     if (method) { | 
					
						
							|  |  |  |       this._options.method = method; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this._options.queryParams = this._queryParams; | 
					
						
							| 
									
										
										
										
											2025-04-03 06:52:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-29 13:19:43 +00:00
										 |  |  |     // Track rate limit attempts separately
 | 
					
						
							|  |  |  |     let rateLimitAttempt = 0; | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     let lastError: Error; | 
					
						
							| 
									
										
										
										
											2025-04-03 06:52:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-29 13:19:43 +00:00
										 |  |  |     // Main retry loop
 | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     for (let attempt = 0; attempt <= this._retries; attempt++) { | 
					
						
							|  |  |  |       try { | 
					
						
							| 
									
										
										
										
											2025-08-18 22:29:24 +00:00
										 |  |  |         // Check if we have a Node.js stream or raw function that needs special handling
 | 
					
						
							|  |  |  |         let requestDataFunc = null; | 
					
						
							|  |  |  |         if ((this._options as any).__nodeStream) { | 
					
						
							|  |  |  |           const nodeStream = (this._options as any).__nodeStream; | 
					
						
							|  |  |  |           requestDataFunc = (req: any) => { | 
					
						
							|  |  |  |             nodeStream.pipe(req); | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |           // Remove the temporary stream reference
 | 
					
						
							|  |  |  |           delete (this._options as any).__nodeStream; | 
					
						
							|  |  |  |         } else if ((this._options as any).__rawStreamFunc) { | 
					
						
							|  |  |  |           requestDataFunc = (this._options as any).__rawStreamFunc; | 
					
						
							|  |  |  |           // Remove the temporary function reference
 | 
					
						
							|  |  |  |           delete (this._options as any).__rawStreamFunc; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         const request = new CoreRequest(this._url, this._options as any, requestDataFunc); | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |         const response = (await request.fire()) as ICoreResponse<R>; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-29 13:19:43 +00:00
										 |  |  |         // Check for 429 status if rate limit handling is enabled
 | 
					
						
							|  |  |  |         if (this._rateLimitConfig && response.status === 429) { | 
					
						
							|  |  |  |           if (rateLimitAttempt >= this._rateLimitConfig.maxRetries) { | 
					
						
							|  |  |  |             // Max rate limit retries reached, return the 429 response
 | 
					
						
							|  |  |  |             return response; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           let waitTime: number; | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |           if ( | 
					
						
							|  |  |  |             this._rateLimitConfig.respectRetryAfter && | 
					
						
							|  |  |  |             response.headers['retry-after'] | 
					
						
							|  |  |  |           ) { | 
					
						
							| 
									
										
										
										
											2025-07-29 13:19:43 +00:00
										 |  |  |             // Parse Retry-After header
 | 
					
						
							|  |  |  |             waitTime = parseRetryAfter(response.headers['retry-after']); | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-29 13:19:43 +00:00
										 |  |  |             // Cap wait time to maxWaitTime
 | 
					
						
							|  |  |  |             waitTime = Math.min(waitTime, this._rateLimitConfig.maxWaitTime); | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             // Use exponential backoff
 | 
					
						
							|  |  |  |             waitTime = Math.min( | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |               this._rateLimitConfig.fallbackDelay * | 
					
						
							|  |  |  |                 Math.pow(this._rateLimitConfig.backoffFactor, rateLimitAttempt), | 
					
						
							|  |  |  |               this._rateLimitConfig.maxWaitTime, | 
					
						
							| 
									
										
										
										
											2025-07-29 13:19:43 +00:00
										 |  |  |             ); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // Call rate limit callback if provided
 | 
					
						
							|  |  |  |           if (this._rateLimitConfig.onRateLimit) { | 
					
						
							|  |  |  |             this._rateLimitConfig.onRateLimit(rateLimitAttempt + 1, waitTime); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // Wait before retrying
 | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |           await new Promise((resolve) => setTimeout(resolve, waitTime)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-29 13:19:43 +00:00
										 |  |  |           rateLimitAttempt++; | 
					
						
							|  |  |  |           // Decrement attempt to retry this attempt
 | 
					
						
							|  |  |  |           attempt--; | 
					
						
							|  |  |  |           continue; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-29 13:19:43 +00:00
										 |  |  |         // Success or non-429 error response
 | 
					
						
							|  |  |  |         return response; | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |       } catch (error) { | 
					
						
							|  |  |  |         lastError = error as Error; | 
					
						
							| 
									
										
										
										
											2025-04-03 06:52:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |         // If this is the last attempt, throw the error
 | 
					
						
							|  |  |  |         if (attempt === this._retries) { | 
					
						
							|  |  |  |           throw lastError; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-04-03 06:52:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |         // Otherwise, wait before retrying
 | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  |         await new Promise((resolve) => setTimeout(resolve, 1000)); | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-04-03 06:52:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-03 06:36:48 +00:00
										 |  |  |     // This should never be reached due to the throw in the loop above
 | 
					
						
							|  |  |  |     throw lastError; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-08-18 00:21:14 +00:00
										 |  |  | } |