@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
JavaScript
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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