UNPKG

@push.rocks/smartrequest

Version:

A module for modern HTTP/HTTPS requests with support for form data, file uploads, JSON, binary data, streams, and more.

411 lines 26 kB
import { CoreRequest, CoreResponse } from '../core/index.js'; import * as plugins from './plugins.js'; import { PaginationStrategy, } from './types/pagination.js'; import { createPaginatedResponse } from './features/pagination.js'; /** * 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) { // Handle array of values (take first) const value = Array.isArray(retryAfter) ? retryAfter[0] : retryAfter; if (!value) return 0; // Try to parse as seconds (number) const seconds = parseInt(value, 10); if (!isNaN(seconds)) { return seconds * 1000; } // Try to parse as HTTP date const retryDate = new Date(value); if (!isNaN(retryDate.getTime())) { return Math.max(0, retryDate.getTime() - Date.now()); } return 0; } /** * Modern fluent client for making HTTP requests */ export class SmartRequest { constructor() { this._options = {}; this._retries = 0; this._queryParams = {}; } /** * Create a new SmartRequest instance */ static create() { return new SmartRequest(); } /** * Set the URL for the request */ url(url) { this._url = url; return this; } /** * Set the HTTP method */ method(method) { this._options.method = method; return this; } /** * Set JSON body for the request */ json(data) { 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) { const form = new plugins.formData(); for (const item of data) { if (Buffer.isBuffer(item.value)) { form.append(item.name, item.value, { filename: item.filename || 'file', contentType: item.contentType || 'application/octet-stream', }); } else { form.append(item.name, item.value); } } if (!this._options.headers) { this._options.headers = {}; } this._options.headers = { ...this._options.headers, ...form.getHeaders(), }; this._options.requestBody = form; return this; } /** * Set raw buffer data for the request */ buffer(data, contentType) { 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, contentType) { 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.pipe === 'function') { // For Node.js streams, we need to use a custom approach // Store the stream to be used later this._options.__nodeStream = stream; } else { // For web ReadableStream, pass directly this._options.requestBody = stream; } return this; } /** * Set request timeout in milliseconds */ timeout(ms) { this._options.timeout = ms; this._options.hardDataCuttingTimeout = ms; return this; } /** * Set number of retry attempts */ retry(count) { this._retries = count; return this; } /** * Enable automatic 429 (Too Many Requests) handling with configurable backoff */ handle429Backoff(config) { this._rateLimitConfig = { maxRetries: config?.maxRetries ?? 3, respectRetryAfter: config?.respectRetryAfter ?? true, maxWaitTime: config?.maxWaitTime ?? 60000, fallbackDelay: config?.fallbackDelay ?? 1000, backoffFactor: config?.backoffFactor ?? 2, onRateLimit: config?.onRateLimit, }; return this; } /** * Set HTTP headers */ headers(headers) { if (!this._options.headers) { this._options.headers = {}; } this._options.headers = { ...this._options.headers, ...headers, }; return this; } /** * Set a single HTTP header */ header(name, value) { if (!this._options.headers) { this._options.headers = {}; } this._options.headers[name] = value; return this; } /** * Set query parameters */ query(params) { this._queryParams = { ...this._queryParams, ...params, }; return this; } /** * Set additional request options */ options(options) { this._options = { ...this._options, ...options, }; return this; } /** * Enable or disable auto-drain for unconsumed response bodies (Node.js only) * Default is true to prevent socket hanging */ autoDrain(enabled) { this._options.autoDrain = enabled; return this; } /** * Set the Accept header to indicate what content type is expected */ accept(type) { // Map response types to Accept header values const acceptHeaders = { json: 'application/json', text: 'text/plain', binary: 'application/octet-stream', stream: '*/*', }; return this.header('Accept', acceptHeaders[type]); } /** * Configure pagination for requests */ pagination(config) { this._paginationConfig = config; return this; } /** * Configure offset-based pagination (page & limit) */ withOffsetPagination(config = {}) { this._paginationConfig = { strategy: PaginationStrategy.OFFSET, pageParam: config.pageParam || 'page', limitParam: config.limitParam || 'limit', startPage: config.startPage || 1, pageSize: config.pageSize || 20, totalPath: config.totalPath || 'total', }; // Add initial pagination parameters this.query({ [this._paginationConfig.pageParam]: String(this._paginationConfig.startPage), [this._paginationConfig.limitParam]: String(this._paginationConfig.pageSize), }); return this; } /** * Configure cursor-based pagination */ withCursorPagination(config = {}) { this._paginationConfig = { strategy: PaginationStrategy.CURSOR, cursorParam: config.cursorParam || 'cursor', cursorPath: config.cursorPath || 'nextCursor', hasMorePath: config.hasMorePath || 'hasMore', }; return this; } /** * Configure Link header-based pagination */ withLinkPagination() { this._paginationConfig = { strategy: PaginationStrategy.LINK_HEADER, }; return this; } /** * Configure custom pagination */ withCustomPagination(config) { this._paginationConfig = { strategy: PaginationStrategy.CUSTOM, hasNextPage: config.hasNextPage, getNextPageParams: config.getNextPageParams, }; return this; } /** * Make a GET request */ async get() { return this.execute('GET'); } /** * Make a POST request */ async post() { return this.execute('POST'); } /** * Make a PUT request */ async put() { return this.execute('PUT'); } /** * Make a DELETE request */ async delete() { return this.execute('DELETE'); } /** * Make a PATCH request */ async patch() { return this.execute('PATCH'); } /** * Get paginated response */ async getPaginated() { if (!this._paginationConfig) { throw new Error('Pagination not configured. Call one of the pagination methods first.'); } // Default to GET if no method specified if (!this._options.method) { this._options.method = 'GET'; } const response = await this.execute(); return await createPaginatedResponse(response, this._paginationConfig, this._queryParams, (nextPageParams) => { // Create a new client with the same configuration but updated query params const nextClient = new SmartRequest(); Object.assign(nextClient, this); nextClient._queryParams = nextPageParams; return nextClient.getPaginated(); }); } /** * Get all pages at once (use with caution for large datasets) */ async getAllPages() { const firstPage = await this.getPaginated(); return firstPage.getAllPages(); } /** * Execute the HTTP request */ async execute(method) { if (method) { this._options.method = method; } this._options.queryParams = this._queryParams; // Track rate limit attempts separately let rateLimitAttempt = 0; let lastError; // Main retry loop for (let attempt = 0; attempt <= this._retries; attempt++) { try { // Check if we have a Node.js stream that needs special handling let requestDataFunc = null; if (this._options.__nodeStream) { const nodeStream = this._options.__nodeStream; requestDataFunc = (req) => { nodeStream.pipe(req); }; // Don't delete __nodeStream yet - let CoreRequest implementations handle it // Node.js will use requestDataFunc, Bun/Deno will convert the stream } const request = new CoreRequest(this._url, this._options, requestDataFunc); // Clean up temporary properties after CoreRequest has been created delete this._options.__nodeStream; const response = (await request.fire()); // 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; if (this._rateLimitConfig.respectRetryAfter && response.headers['retry-after']) { // Parse Retry-After header waitTime = parseRetryAfter(response.headers['retry-after']); // Cap wait time to maxWaitTime waitTime = Math.min(waitTime, this._rateLimitConfig.maxWaitTime); } else { // Use exponential backoff waitTime = Math.min(this._rateLimitConfig.fallbackDelay * Math.pow(this._rateLimitConfig.backoffFactor, rateLimitAttempt), this._rateLimitConfig.maxWaitTime); } // Call rate limit callback if provided if (this._rateLimitConfig.onRateLimit) { this._rateLimitConfig.onRateLimit(rateLimitAttempt + 1, waitTime); } // Wait before retrying await new Promise((resolve) => setTimeout(resolve, waitTime)); rateLimitAttempt++; // Decrement attempt to retry this attempt attempt--; continue; } // Success or non-429 error response return response; } catch (error) { lastError = error; // If this is the last attempt, throw the error if (attempt === this._retries) { throw lastError; } // Otherwise, wait before retrying await new Promise((resolve) => setTimeout(resolve, 1000)); } } // This should never be reached due to the throw in the loop above throw lastError; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRyZXF1ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvY2xpZW50L3NtYXJ0cmVxdWVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsV0FBVyxFQUFFLFlBQVksRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRTdELE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBU3hDLE9BQU8sRUFFTCxrQkFBa0IsR0FLbkIsTUFBTSx1QkFBdUIsQ0FBQztBQUMvQixPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUVuRTs7OztHQUlHO0FBQ0gsU0FBUyxlQUFlLENBQUMsVUFBNkI7SUFDcEQsc0NBQXNDO0lBQ3RDLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDO0lBRXJFLElBQUksQ0FBQyxLQUFLO1FBQUUsT0FBTyxDQUFDLENBQUM7SUFFckIsbUNBQW1DO0lBQ25DLE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDcEMsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1FBQ3BCLE9BQU8sT0FBTyxHQUFHLElBQUksQ0FBQztJQUN4QixDQUFDO0lBRUQsNEJBQTRCO0lBQzVCLE1BQU0sU0FBUyxHQUFHLElBQUksSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ2xDLElBQUksQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLE9BQU8sRUFBRSxDQUFDLEVBQUUsQ0FBQztRQUNoQyxPQUFPLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLFNBQVMsQ0FBQyxPQUFPLEVBQUUsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQztJQUN2RCxDQUFDO0lBRUQs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