@push.rocks/smartrequest
A module for modern HTTP/HTTPS requests with support for form data, file uploads, JSON, binary data, streams, and more.
readme.md for @push.rocks/smartrequest
A modern, cross-platform HTTP/HTTPS request library for Node.js, Bun, Deno, and browsers with a unified API, supporting form data, file uploads, JSON, binary data, streams, and unix sockets.
Install
# Using npm
npm install @push.rocks/smartrequest --save
# Using pnpm
pnpm add @push.rocks/smartrequest
# Using yarn
yarn add @push.rocks/smartrequest
Key Features
- 🚀 Modern Fetch-like API - Familiar response methods (
.json(),.text(),.arrayBuffer(),.stream()) - 🌐 Cross-Platform - Works in Node.js, Bun, Deno, and browsers with a unified API
- 🔌 Unix Socket Support - Connect to local services like Docker (Node.js, Bun, and Deno)
- 📦 Form Data & File Uploads - Built-in support for multipart/form-data
- 🔁 Pagination Support - Multiple strategies (offset, cursor, Link headers)
- ⚡ Keep-Alive Connections - Efficient connection pooling in Node.js
- 🛡️ TypeScript First - Full type safety and IntelliSense support
- 🎯 Zero Magic Defaults - Explicit configuration following fetch API principles
- 📡 Streaming Support - Stream buffers, files, and custom data without loading into memory
- 🔧 Highly Configurable - Timeouts, retries, headers, rate limiting, and more
Architecture
SmartRequest features a multi-layer architecture that provides consistent behavior across platforms:
- Core Base - Abstract classes and unified types shared across implementations
- Core Node - Node.js implementation using native http/https modules with unix socket support
- Core Bun - Bun implementation using native fetch with unix socket support via
unixoption - Core Deno - Deno implementation using fetch with unix socket support via HttpClient proxy
- Core Fetch - Browser implementation using the Fetch API
- Core - Dynamic runtime detection and implementation selection using @push.rocks/smartenv
- Client - High-level fluent API for everyday use
The library automatically detects the runtime environment (Deno, Bun, Node.js, or browser) and loads the appropriate implementation, ensuring optimal performance and native feature support for each platform.
Usage
@push.rocks/smartrequest provides a clean, type-safe API inspired by the native fetch API but with additional features needed for modern applications.
Basic Usage
import { SmartRequest } from '@push.rocks/smartrequest';
// Simple GET request
async function fetchUserData(userId: number) {
const response = await SmartRequest.create()
.url(`https://jsonplaceholder.typicode.com/users/${userId}`)
.get();
// Use the fetch-like response API
const userData = await response.json();
console.log(userData); // The parsed JSON response
}
// POST request with JSON body
async function createPost(title: string, body: string, userId: number) {
const response = await SmartRequest.create()
.url('https://jsonplaceholder.typicode.com/posts')
.json({ title, body, userId })
.post();
const createdPost = await response.json();
console.log(createdPost); // The created post
}
Direct Core API Usage
For advanced use cases, you can use the Core API directly:
import { CoreRequest } from '@push.rocks/smartrequest';
async function directCoreRequest() {
const request = new CoreRequest('https://api.example.com/data', {
method: 'GET',
headers: {
Accept: 'application/json',
},
});
const response = await request.fire();
const data = await response.json();
return data;
}
Setting Headers and Query Parameters
import { SmartRequest } from '@push.rocks/smartrequest';
async function searchRepositories(query: string, perPage: number = 10) {
const response = await SmartRequest.create()
.url('https://api.github.com/search/repositories')
.header('Accept', 'application/vnd.github.v3+json')
.query({
q: query,
per_page: perPage.toString(),
})
.get();
const data = await response.json();
return data.items;
}
Handling Timeouts and Retries
import { SmartRequest } from '@push.rocks/smartrequest';
async function fetchWithRetry(url: string) {
const response = await SmartRequest.create()
.url(url)
.timeout(5000) // 5 seconds timeout
.retry(3) // Retry up to 3 times on failure
.get();
return await response.json();
}
Setting Request Options
Use the options() method to set any request options supported by the underlying implementation:
import { SmartRequest } from '@push.rocks/smartrequest';
// Set various options
const response = await SmartRequest.create()
.url('https://api.example.com/data')
.options({
keepAlive: true, // Enable connection reuse (Node.js)
timeout: 10000, // 10 second timeout
hardDataCuttingTimeout: 15000, // 15 second hard timeout
// Platform-specific options are also supported
})
.get();
Working with Different Response Types
The API provides a fetch-like interface for handling different response types:
import { SmartRequest } from '@push.rocks/smartrequest';
// JSON response (default)
async function fetchJson(url: string) {
const response = await SmartRequest.create().url(url).get();
return await response.json(); // Parses JSON automatically
}
// Text response
async function fetchText(url: string) {
const response = await SmartRequest.create().url(url).get();
return await response.text(); // Returns response as string
}
// Binary data
async function downloadImage(url: string) {
const response = await SmartRequest.create()
.url(url)
.accept('binary') // Optional: hints to server we want binary
.get();
const buffer = await response.arrayBuffer();
return Buffer.from(buffer); // Convert ArrayBuffer to Buffer if needed
}
// Streaming response (Web Streams API - cross-platform)
async function streamLargeFile(url: string) {
const response = await SmartRequest.create().url(url).get();
// Get a web-style ReadableStream (works everywhere)
const stream = response.stream();
if (stream) {
const reader = stream.getReader();
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log(`Received ${value.length} bytes of data`);
}
} finally {
reader.releaseLock();
}
}
}
// Convert to Node.js stream if needed (Node.js only)
async function streamWithNodeApi(url: string) {
const response = await SmartRequest.create().url(url).get();
// Convert web stream to Node.js stream
import { Readable } from 'stream';
const webStream = response.stream();
const nodeStream = Readable.fromWeb(webStream);
nodeStream.on('data', (chunk) => {
console.log(`Received ${chunk.length} bytes of data`);
});
return new Promise((resolve, reject) => {
nodeStream.on('end', resolve);
nodeStream.on('error', reject);
});
}
Response Object Methods
The response object provides these methods:
json<T>(): Promise<T>- Parse response as JSONtext(): Promise<string>- Get response as textarrayBuffer(): Promise<ArrayBuffer>- Get response as ArrayBufferstream(): ReadableStream<Uint8Array> | null- Get web-style ReadableStream (cross-platform)raw(): Response | http.IncomingMessage- Get the underlying platform response object
Each body method can only be called once per response, similar to the fetch API.
Important: Always Consume Response Bodies
You should always consume response bodies, even if you don't need the data. Unconsumed response bodies can cause:
- Memory leaks as data accumulates in buffers
- Socket hanging with keep-alive connections
- Connection pool exhaustion
// ❌ BAD - Response body is not consumed
const response = await SmartRequest.create()
.url('https://api.example.com/status')
.get();
if (response.ok) {
console.log('Success!');
}
// Socket may hang here!
// ✅ GOOD - Response body is consumed
const response = await SmartRequest.create()
.url('https://api.example.com/status')
.get();
if (response.ok) {
console.log('Success!');
}
await response.text(); // Consume the body even if not needed
In Node.js, SmartRequest automatically drains unconsumed responses to prevent socket hanging, but it's still best practice to explicitly consume response bodies. When auto-drain occurs, you'll see a console log: Auto-draining unconsumed response body for [URL] (status: [STATUS]).
You can disable auto-drain if needed:
// Disable auto-drain (not recommended unless you have specific requirements)
const response = await SmartRequest.create()
.url('https://api.example.com/data')
.autoDrain(false) // Disable auto-drain
.get();
// Now you MUST consume the body or the socket will hang
await response.text();
Advanced Features
Form Data with File Uploads
import { SmartRequest } from '@push.rocks/smartrequest';
import * as fs from 'fs';
async function uploadMultipleFiles(
files: Array<{ name: string; path: string }>,
) {
const formFields = files.map((file) => ({
name: 'files',
value: fs.readFileSync(file.path),
filename: file.name,
contentType: 'application/octet-stream',
}));
const response = await SmartRequest.create()
.url('https://api.example.com/upload')
.formData(formFields)
.post();
return await response.json();
}
Streaming Request Bodies
SmartRequest provides multiple ways to stream data in requests, making it easy to upload large files or send real-time data without loading everything into memory:
import { SmartRequest } from '@push.rocks/smartrequest';
import * as fs from 'fs';
import { Readable } from 'stream';
// Stream a Buffer directly (works everywhere)
async function uploadBuffer() {
const buffer = Buffer.from('Hello, World!');
const response = await SmartRequest.create()
.url('https://api.example.com/upload')
.buffer(buffer, 'text/plain')
.post();
return await response.json();
}
// Stream using web ReadableStream (cross-platform!)
async function uploadWebStream() {
const stream = new ReadableStream({
start(controller) {
const data = new TextEncoder().encode('Stream data');
controller.enqueue(data);
controller.close();
},
});
const response = await SmartRequest.create()
.url('https://api.example.com/upload')
.stream(stream, 'text/plain')
.post();
return await response.json();
}
// Stream a file using Node.js streams (Node.js only)
async function uploadLargeFile(filePath: string) {
const fileStream = fs.createReadStream(filePath);
const response = await SmartRequest.create()
.url('https://api.example.com/upload')
.stream(fileStream, 'application/octet-stream')
.post();
return await response.json();
}
// Stream data from any readable source (Node.js only)
async function streamData(dataSource: Readable) {
const response = await SmartRequest.create()
.url('https://api.example.com/stream')
.stream(dataSource)
.post();
return await response.json();
}
// Send Uint8Array (works everywhere)
async function uploadBinaryData() {
const data = new Uint8Array([72, 101, 108, 108, 111]); // "Hello"
const response = await SmartRequest.create()
.url('https://api.example.com/binary')
.buffer(data, 'application/octet-stream')
.post();
return await response.json();
}
Streaming Methods
-
.buffer(data, contentType?)- Stream a Buffer or Uint8Array directlydata: Buffer (Node.js) or Uint8Array (cross-platform) to sendcontentType: Optional content type (defaults to 'application/octet-stream')- ✅ Works everywhere (Node.js, Bun, Deno, browsers)
-
.stream(stream, contentType?)- Stream from ReadableStream or Node.js streamstream: Web ReadableStream (cross-platform) or Node.js stream (Node.js only)contentType: Optional content type- ✅ Web ReadableStream works everywhere (Node.js, Bun, Deno, browsers)
- ⚠️ Node.js streams only work in Node.js (automatically converted to web streams in Bun/Deno)
These methods are particularly useful for:
- Uploading large files without loading them into memory
- Streaming real-time data to servers
- Proxying data between services
- Implementing chunked transfer encoding
Unix Socket Support (Node.js, Bun, and Deno)
SmartRequest supports unix sockets across all server-side runtimes with a unified API:
import { SmartRequest } from '@push.rocks/smartrequest';
// Connect to a service via Unix socket (works on Node.js, Bun, and Deno)
async function queryViaUnixSocket() {
const response = await SmartRequest.create()
.url('http://unix:/var/run/docker.sock:/v1.24/containers/json')
.get();
return await response.json();
}
// Alternative: Use socketPath option (works on all server runtimes)
async function queryWithSocketPath() {
const response = await SmartRequest.create()
.url('http://localhost/version')
.options({ socketPath: '/var/run/docker.sock' })
.get();
return await response.json();
}
Runtime-Specific Unix Socket APIs
Each runtime implements unix sockets using its native capabilities:
Bun:
import { CoreRequest } from '@push.rocks/smartrequest/core_bun';
// Bun uses the native `unix` fetch option
const response = await CoreRequest.create('http://localhost/version', {
unix: '/var/run/docker.sock'
});
Deno:
import { CoreRequest } from '@push.rocks/smartrequest/core_deno';
// Deno uses HttpClient with unix socket proxy
const client = Deno.createHttpClient({
proxy: { url: 'unix:///var/run/docker.sock' }
});
const response = await CoreRequest.create('http://localhost/version', {
client
});
// Clean up when done
client.close();
Node.js:
import { CoreRequest } from '@push.rocks/smartrequest/core_node';
// Node.js uses native socketPath option
const response = await CoreRequest.create('http://localhost/version', {
socketPath: '/var/run/docker.sock'
});
Pagination Support
The library includes built-in support for various pagination strategies:
import { SmartRequest } from '@push.rocks/smartrequest';
// Offset-based pagination (page & limit)
async function fetchAllUsers() {
const client = SmartRequest.create()
.url('https://api.example.com/users')
.withOffsetPagination({
pageParam: 'page',
limitParam: 'limit',
startPage: 1,
pageSize: 20,
totalPath: 'meta.total',
});
// Get first page with pagination info
const firstPage = await client.getPaginated();
console.log(`Found ${firstPage.items.length} users on first page`);
console.log(`Has more pages: ${firstPage.hasNextPage}`);
if (firstPage.hasNextPage) {
// Get next page
const secondPage = await firstPage.getNextPage();
console.log(`Found ${secondPage.items.length} more users`);
}
// Or get all pages at once (use with caution for large datasets)
const allUsers = await client.getAllPages();
console.log(`Retrieved ${allUsers.length} users in total`);
}
// Cursor-based pagination
async function fetchAllPosts() {
const allPosts = await SmartRequest.create()
.url('https://api.example.com/posts')
.withCursorPagination({
cursorParam: 'cursor',
cursorPath: 'meta.nextCursor',
hasMorePath: 'meta.hasMore',
})
.getAllPages();
console.log(`Retrieved ${allPosts.length} posts in total`);
}
// Link header-based pagination (GitHub API style)
async function fetchAllIssues(repo: string) {
const paginatedResponse = await SmartRequest.create()
.url(`https://api.github.com/repos/${repo}/issues`)
.header('Accept', 'application/vnd.github.v3+json')
.withLinkPagination()
.getPaginated();
return paginatedResponse.getAllPages();
}
Keep-Alive Connections (Node.js)
import { SmartRequest } from '@push.rocks/smartrequest';
// Enable keep-alive for better performance with multiple requests
async function performMultipleRequests() {
// Note: keepAlive is NOT enabled by default
const response1 = await SmartRequest.create()
.url('https://api.example.com/endpoint1')
.options({ keepAlive: true })
.get();
const response2 = await SmartRequest.create()
.url('https://api.example.com/endpoint2')
.options({ keepAlive: true })
.get();
// Connections are pooled and reused when keepAlive is enabled
return [await response1.json(), await response2.json()];
}
Rate Limiting (429 Too Many Requests) Handling
The library includes built-in support for handling HTTP 429 (Too Many Requests) responses with intelligent backoff:
import { SmartRequest } from '@push.rocks/smartrequest';
// Simple usage - handle 429 with defaults
async function fetchWithRateLimitHandling() {
const response = await SmartRequest.create()
.url('https://api.example.com/data')
.handle429Backoff() // Automatically retry on 429
.get();
return await response.json();
}
// Advanced usage with custom configuration
async function fetchWithCustomRateLimiting() {
const response = await SmartRequest.create()
.url('https://api.example.com/data')
.handle429Backoff({
maxRetries: 5, // Try up to 5 times (default: 3)
respectRetryAfter: true, // Honor Retry-After header (default: true)
maxWaitTime: 30000, // Max 30 seconds wait (default: 60000)
fallbackDelay: 2000, // 2s initial delay if no Retry-After (default: 1000)
backoffFactor: 2, // Exponential backoff multiplier (default: 2)
onRateLimit: (attempt, waitTime) => {
console.log(`Rate limited. Attempt ${attempt}, waiting ${waitTime}ms`);
},
})
.get();
return await response.json();
}
// Example: API client with rate limit handling
class RateLimitedApiClient {
private async request(path: string) {
return SmartRequest.create()
.url(`https://api.example.com${path}`)
.handle429Backoff({
maxRetries: 3,
onRateLimit: (attempt, waitTime) => {
console.log(
`API rate limit hit. Waiting ${waitTime}ms before retry ${attempt}`,
);
},
});
}
async fetchData(id: string) {
const response = await this.request(`/data/${id}`).get();
return response.json();
}
}
The rate limiting feature:
- Automatically detects 429 responses and retries with backoff
- Respects the
Retry-Afterheader when present (supports both seconds and HTTP date formats) - Uses exponential backoff when no
Retry-Afterheader is provided - Allows custom callbacks for monitoring rate limit events
- Caps maximum wait time to prevent excessive delays
Platform-Specific Features
Browser-Specific Options
When running in a browser, you can use browser-specific fetch options:
const response = await SmartRequest.create()
.url('https://api.example.com/data')
.options({
credentials: 'include', // Include cookies
mode: 'cors', // CORS mode
cache: 'no-cache', // Cache mode
referrerPolicy: 'no-referrer',
})
.get();
Node.js-Specific Options
When running in Node.js, you can use Node-specific options:
import { Agent } from 'https';
const response = await SmartRequest.create()
.url('https://api.example.com/data')
.options({
agent: new Agent({ keepAlive: true }), // Custom agent
socketPath: '/var/run/api.sock', // Unix socket
})
.get();
Bun-Specific Options
When running in Bun, you can use Bun-specific options:
const response = await SmartRequest.create()
.url('https://api.example.com/data')
.options({
unix: '/var/run/api.sock', // Unix socket (Bun's native option)
keepAlive: true, // Keep-alive support
})
.get();
// Bun uses web streams natively
const streamResponse = await SmartRequest.create()
.url('https://api.example.com/data')
.get();
const webStream = streamResponse.stream(); // ✅ Use web streams in Bun
Deno-Specific Options
When running in Deno, you can use Deno-specific options:
// Custom HttpClient for advanced configuration
const client = Deno.createHttpClient({
proxy: { url: 'unix:///var/run/api.sock' }
});
const response = await SmartRequest.create()
.url('https://api.example.com/data')
.options({
client, // Custom Deno HttpClient
})
.get();
// Remember to clean up clients when done
client.close();
// Deno uses web streams natively
const streamResponse = await SmartRequest.create()
.url('https://api.example.com/data')
.get();
const webStream = streamResponse.stream(); // ✅ Use web streams in Deno
Complete Example: Building a REST API Client
Here's a complete example of building a typed API client:
import { SmartRequest, type ICoreResponse } from '@push.rocks/smartrequest';
interface User {
id: number;
name: string;
email: string;
}
interface Post {
id: number;
title: string;
body: string;
userId: number;
}
class BlogApiClient {
private baseUrl = 'https://jsonplaceholder.typicode.com';
private async request(path: string) {
return SmartRequest.create()
.url(`${this.baseUrl}${path}`)
.header('Accept', 'application/json');
}
async getUser(id: number): Promise<User> {
const response = await this.request(`/users/${id}`).get();
return response.json<User>();
}
async createPost(post: Omit<Post, 'id'>): Promise<Post> {
const response = await this.request('/posts').json(post).post();
return response.json<Post>();
}
async deletePost(id: number): Promise<void> {
const response = await this.request(`/posts/${id}`).delete();
if (!response.ok) {
throw new Error(`Failed to delete post: ${response.statusText}`);
}
// Consume the body
await response.text();
}
async getAllPosts(userId?: number): Promise<Post[]> {
const client = this.request('/posts');
if (userId) {
client.query({ userId: userId.toString() });
}
const response = await client.get();
return response.json<Post[]>();
}
}
// Usage
const api = new BlogApiClient();
const user = await api.getUser(1);
const posts = await api.getAllPosts(user.id);
Error Handling
import { SmartRequest } from '@push.rocks/smartrequest';
async function fetchWithErrorHandling(url: string) {
try {
const response = await SmartRequest.create()
.url(url)
.timeout(5000)
.retry(2)
.get();
// Check if request was successful
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
// Handle different content types
const contentType = response.headers['content-type'];
if (contentType?.includes('application/json')) {
return await response.json();
} else if (contentType?.includes('text/')) {
return await response.text();
} else {
return await response.arrayBuffer();
}
} catch (error) {
if (error.code === 'ECONNREFUSED') {
console.error('Connection refused - is the server running?');
} else if (error.code === 'ETIMEDOUT') {
console.error('Request timed out');
} else if (error.name === 'AbortError') {
console.error('Request was aborted');
} else {
console.error('Request failed:', error.message);
}
throw error;
}
}
Migrating from Earlier Versions
From v4.x to v5.x
Version 5.0 completes the transition to modern web standards by removing Node.js-specific streaming APIs:
Breaking Changes
-
.streamNode()Method Removed- The
.streamNode()method has been removed from all response objects - Use the cross-platform
.stream()method instead, which returns a webReadableStream<Uint8Array> - For Node.js users who need Node.js streams, convert using
Readable.fromWeb()
// ❌ Before (v4.x) - Node.js only const response = await SmartRequest.create().url(url).get(); const nodeStream = response.streamNode(); // ✅ After (v5.x) - Cross-platform import { Readable } from 'stream'; const response = await SmartRequest.create().url(url).get(); const webStream = response.stream(); const nodeStream = Readable.fromWeb(webStream); // Convert to Node.js stream - The
-
Request
.raw()Method Removed- The
.raw(streamFunc)method has been removed from the SmartRequest client - Use
.stream()with a webReadableStreaminstead for request body streaming - Node.js users can create web streams from Node.js streams using
Readable.toWeb()
// ❌ Before (v4.x) - Node.js only const response = await SmartRequest.create() .url(url) .raw((request) => { request.write('chunk1'); request.write('chunk2'); request.end(); }) .post(); // ✅ After (v5.x) - Cross-platform const stream = new ReadableStream({ start(controller) { controller.enqueue(new TextEncoder().encode('chunk1')); controller.enqueue(new TextEncoder().encode('chunk2')); controller.close(); } }); const response = await SmartRequest.create() .url(url) .stream(stream) .post(); // Or convert from Node.js stream (Node.js only) import { Readable } from 'stream'; import * as fs from 'fs'; const nodeStream = fs.createReadStream('file.txt'); const webStream = Readable.toWeb(nodeStream); const response = await SmartRequest.create() .url(url) .stream(webStream) .post(); - The
-
Response
.raw()Method Preserved- The
response.raw()method is still available for accessing platform-specific response objects - Returns
http.IncomingMessagein Node.js orResponsein other runtimes - Use for advanced scenarios requiring access to raw platform objects
// ✅ Still works in v5.x const response = await SmartRequest.create().url(url).get(); const rawResponse = response.raw(); // http.IncomingMessage or Response - The
Migration Guide
For Response Streaming:
// Before (v4.x)
const response = await SmartRequest.create().url(url).get();
const nodeStream = response.streamNode();
nodeStream.on('data', (chunk) => {
console.log(`Received ${chunk.length} bytes`);
});
// After (v5.x) - Option 1: Use web streams directly
const response = await SmartRequest.create().url(url).get();
const webStream = response.stream();
if (webStream) {
const reader = webStream.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log(`Received ${value.length} bytes`);
}
reader.releaseLock();
}
// After (v5.x) - Option 2: Convert to Node.js stream (Node.js only)
import { Readable } from 'stream';
const response = await SmartRequest.create().url(url).get();
const webStream = response.stream();
const nodeStream = Readable.fromWeb(webStream);
nodeStream.on('data', (chunk) => {
console.log(`Received ${chunk.length} bytes`);
});
For Request Streaming:
Node.js streams are still accepted by the .stream() method and automatically converted internally. No changes required for most use cases:
// ✅ Still works in v5.x
import * as fs from 'fs';
const fileStream = fs.createReadStream('large-file.bin');
const response = await SmartRequest.create()
.url('https://api.example.com/upload')
.stream(fileStream, 'application/octet-stream')
.post();
Benefits:
- ✅ True cross-platform compatibility
- ✅ Modern web standards
- ✅ Cleaner API surface
- ✅ Single streaming approach works everywhere
From v3.x to v4.x
Version 4.0 adds comprehensive cross-platform support:
- Multi-Runtime Support: Now works natively in Node.js, Bun, Deno, and browsers
- Unix Sockets Everywhere: Unix socket support added for Bun and Deno
- Web Streams: Full support for web ReadableStream across all platforms
- Automatic Runtime Detection: No configuration needed - works everywhere automatically
From v2.x to v3.x
Version 3.0 brought significant architectural improvements:
- Legacy API Removed: The function-based API (getJson, postJson, etc.) has been removed. Use SmartRequest instead.
- Unified Response API: All responses now use the same fetch-like interface regardless of platform.
- Stream Changes: The
stream()method now returns a web-style ReadableStream on all platforms. UsestreamNode()for Node.js streams. - Cross-Platform by Default: The library now works in browsers out of the box with automatic platform detection.
License and Legal Information
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the license file within this repository.
Please note: The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
Trademarks
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
Company Information
Task Venture Capital GmbH
Registered at District court Bremen HRB 35230 HB, Germany
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
changelog.md for @push.rocks/smartrequest
2025-11-17 - 5.0.1 - fix(test)
Enable --logfile in test script and bump @git.zone/tstest to ^2.8.2
- Update npm script: add --logfile flag to the test command to produce test logs
- Bump devDependency @git.zone/tstest from ^2.8.1 to ^2.8.2
2025-11-17 - 5.0.0 - BREAKING CHANGE(client/streaming)
Unify streaming APIs: remove raw()/streamNode() and standardize on web ReadableStream across runtimes
- Removed SmartRequest.raw() and RawStreamFunction type. The raw streaming function API is gone — use .stream() with a web ReadableStream for request body streaming.
- Removed response.streamNode() from all runtimes. Responses now expose only response.stream() (ReadableStream<Uint8Array>). Node.js consumers must convert using Readable.fromWeb() if a Node.js stream is required.
- Node implementation now uses Readable.toWeb() to convert native Node streams into web ReadableStream for a single cross-platform streaming API.
- Client request.stream() still accepts Node.js streams but they are converted internally to web streams; temporary internal properties for raw streaming were removed.
- Updated tests and documentation (readme) with migration guidance and examples for converting between web and Node.js streams.
- Bumped devDependencies (@git.zone/tsbuild, tsrun, tstest) and upgraded form-data to a newer patch release.
2025-11-16 - 4.4.2 - fix(core_base/request)
Strip 'unix:' prefix when parsing unix socket URLs so socketPath is a clean filesystem path
- CoreRequest.parseUnixSocketUrl now removes a leading 'unix:' prefix and returns socketPath as a filesystem path (e.g., /var/run/docker.sock)
- Updated tests for Bun, Deno and Node to expect socketPath without the 'unix:' prefix
- Adjusted comments/documentation in core_base/request.ts to clarify returned socketPath format
2025-11-16 - 4.4.1 - fix(core_node)
Fix unix socket URL parsing and handling in CoreRequest
- CoreRequest.parseUnixSocketUrl now strips http:// and https:// prefixes so it correctly parses both full URLs (e.g. http://unix:/path/to/socket:/route) and already-stripped unix: paths.
- Node.js CoreRequest now passes the original request URL to parseUnixSocketUrl instead of options.path, preventing incorrect socketPath/path extraction.
- Fixes connection failures when using unix socket URLs (for example when targeting Docker via http://unix:/var/run/docker.sock:/v1.24/...).
2025-11-16 - 4.4.0 - feat(core)
Add Bun and Deno runtime support, unify core loader, unix-socket support and cross-runtime streaming/tests
- package.json: expose ./core_bun and ./core_deno in exports and add runtime-related keywords
- Core dynamic loader (ts/core/index.ts): detect Bun and Deno at runtime and load corresponding implementations
- New runtime modules: added ts/core_bun/* and ts/core_deno/* (response, types, index) to provide Bun and Deno CoreResponse/CoreRequest wrappers
- Client streaming: SmartRequest no longer immediately deletes temporary __nodeStream and __rawStreamFunc props — CoreRequest implementations handle them; temporary properties are cleaned up after CoreRequest is created
- Node.js request: core_node/request.ts converts web ReadableStream to Node.js Readable via stream.Readable.fromWeb and pipes it; also supports passing requestDataFunc for raw streaming
- core_node/plugins: export stream helper and rework third-party exports (agentkeepalive, form-data) for Node implementation
- CoreResponse for Bun/Deno: new implementations wrap native fetch Response and expose raw(), stream(), and streamNode() behavior (streamNode() throws in Bun/Deno with guidance to use web streams)
- Tests: added unified cross-runtime streaming tests and separate unix-socket tests for Node/Bun/Deno with Docker-socket availability checks; removed old node-only streaming test
- Docs/readme: updated to describe Node, Bun, Deno, and browser support, unix socket behavior per runtime, and new test conventions
2025-11-16 - 4.3.8 - fix(core)
Ensure correct ArrayBuffer return, fix fetch body typing, reorganize node-only tests, and bump tsbuild devDependency
- core_node: Fix arrayBuffer() to ensure an ArrayBuffer is returned (avoid returning SharedArrayBuffer) to improve interoperability when consuming binary responses.
- core_fetch: Cast request body to BodyInit when assigning to fetch options and preserve duplex = 'half' for ReadableStream bodies to satisfy typings and streaming behavior.
- tests: Reorganize tests into Node-only variants (rename/remove multi-platform test files to test.*.node.ts) to separate platform-specific test coverage.
- chore: Bump devDependency @git.zone/tsbuild from ^2.6.8 to ^2.7.1.
2025-11-01 - 4.3.7 - fix(ci)
Update dependencies, add deno.lock, and reorganize tests for browser and Node environments
- Add deno.lock with resolved npm package versions for deterministic Deno/npm usage
- Bump @push.rocks/smartenv dependency to ^6.0.0
- Bump devDependencies: @git.zone/tsbuild -> ^2.6.8, @git.zone/tsrun -> ^1.6.2, @git.zone/tstest -> ^2.7.0
- Reorganize tests: move browser tests to chromium variants and add environment-specific test files for node, bun, deno (streaming, timeout, streamNode, etc.)
- Update package.json dependency ranges to match upgraded lockfile and test tooling
2025-10-26 - 4.3.6 - fix(ci)
Use .npmrc for registry authentication in Gitea workflow and add conditional npmjs publish
- Replace npm config set commands with creating a .npmrc file for Gitea registry authentication in .gitea/workflows/default_tags.yaml
- Add conditional update of .npmrc and publishing to npmjs.org when NPMCI_TOKEN_NPM is provided
- Keep pnpm publish --no-git-checks; improve CI credential handling to be file-based
2025-10-26 - 4.3.5 - fix(workflows)
Remove npmci wrappers from CI workflows and use pnpm/npm CLI directly
- Removed global npmci installation and npmci npm prepare steps from Gitea workflow files
- Use pnpm install/test/build instead of npmci-wrapped commands in test jobs
- Replace npmci command npm config set ... with direct npm config set calls for registry/auth configuration
- Use pnpm publish --no-git-checks for Gitea publishing and use pnpm publish for conditional npmjs publish when token present
- Simplified dependency auditing to run pnpm audit and set registry via npm config set
- Install tsdoc globally and run tsdoc during docs build step (replacing npmci command usage)
2025-10-25 - 4.3.4 - fix(ci)
Fix Gitea workflow publish invocation to run npm publish via npmci command
- Update .gitea/workflows/default_tags.yaml to use 'npmci command npm publish' for the publish step
- Ensures the workflow runs npm publish through the npmci command wrapper to avoid incorrect task invocation
2025-10-25 - 4.3.3 - fix(ci)
Improve Gitea release workflow: install deps, configure Gitea npm registry, and optionally publish to npmjs.org
- Run npm install in the release job to ensure dependencies are available before publishing.
- Configure Gitea/npm registry using GITHUB_SERVER_URL and set auth token for the @ scope.
- Publish to the Gitea npm registry during release.
- If NPMCI_TOKEN_NPM is provided, also publish to the public npmjs.org registry (conditional publish).
- Extract host from GITHUB_SERVER_URL to correctly set the registry auth URL.
2025-10-17 - 4.3.2 - fix(core)
Remove stray console.log from core module
- Removed a stray debug console.log(modulePath) from ts/core/index.ts that printed the module path during Node environment initialization
2025-08-19 - 4.3.1 - fix(core)
Improve streaming support and timeout handling; add browser streaming & timeout tests and README clarifications
- core_fetch: accept Uint8Array and Buffer-like bodies; set fetch duplex for ReadableStream bodies so streaming requests work in environments that require duplex
- core_fetch: implement AbortController-based timeouts and ensure timeouts are cleared on success/error to avoid hanging timers
- core_node: add explicit request timeout handling (request.setTimeout) and hard-data-cutting timeout tracking with proper timeoutId clear on success/error
- client: document that raw(streamFunc) is Node-only (not supported in browsers)
- tests: add browser streaming tests (test/test.streaming.browser.ts) that exercise buffer() and web ReadableStream via stream()
- tests: add timeout tests (test/test.timeout.ts) to validate clearing timers, enforcing timeouts, and preventing timer leaks across multiple requests
- docs: update README streaming section to clarify cross-platform behavior of buffer(), stream(), and raw() methods
2025-08-18 - 4.3.0 - feat(client/smartrequest)
Add streaming and raw buffer support to SmartRequest (buffer, stream, raw); update docs and tests
- Add SmartRequest.buffer(data, contentType?) to send Buffer or Uint8Array bodies with Content-Type header.
- Add SmartRequest.stream(stream, contentType?) to accept Node.js Readable streams or web ReadableStream and set Content-Type when provided.
- Add SmartRequest.raw(streamFunc) to allow custom raw streaming functions (Node.js only) and a RawStreamFunction type.
- Wire Node.js stream handling into CoreRequest by passing a requestDataFunc when creating CoreRequest instances.
- Add comprehensive streaming examples and documentation to README describing buffer/stream/raw usage and streaming methods.
- Add tests for streaming behavior (test/test.streaming.ts) covering buffer, stream, raw, and Uint8Array usage.
- Update client exports and plugins to support streaming features and FormData usage where needed.
2025-08-18 - 4.2.2 - fix(client)
Fix CI configuration, prevent socket hangs with auto-drain, and apply various client/core TypeScript fixes and test updates
- CI/workflow updates: switch container IMAGE to code.foss.global/host.today/ht-docker-node:npmci, adjust NPMCI_COMPUTED_REPOURL, and install @ship.zone/npmci instead of @shipzone/npmci
- Prevent socket hanging by adding automatic draining of unconsumed Node.js response bodies (configurable via options.autoDrain / SmartRequest.autoDrain); added logging when auto-drain runs and updated tests to consume bodies
- Client improvements: fixes and cleanups in SmartRequest (accept header mapping, formData header handling, options(), pagination helpers, handle429Backoff backoff/Retry-After parsing and callbacks, retry logic and small API ergonomics)
- Core fixes: fetch and node implementations corrected (buildUrl, fetch options, request/response constructors, stream conversions to web ReadableStream, proper error messages) and consistent exports
- TypeScript and formatting fixes across many files (consistent trailing commas, object layout, newline fixes, typed function signatures, cleaned up exports and module imports)
- Package metadata and tooling updates: package.json bug/homepage URLs adjusted to code.foss.global, bumped @git.zone/tstest devDependency, added pnpm overrides field; small .gitignore additions
2025-07-29 - 4.2.1 - fix(client)
Fix socket hanging issues and add auto-drain feature
Fixes:
- Fixed socket hanging issues caused by unconsumed response bodies
- Resolved test timeout problems where sockets remained open after tests completed
Features:
- Added automatic response body draining to prevent socket pool exhaustion
- Made auto-drain configurable via
autoDrain()method (enabled by default) - Added logging when auto-drain activates for debugging purposes
Improvements:
- Updated all tests to properly consume response bodies
- Enhanced documentation about the importance of consuming response bodies
2025-07-29 - 4.2.0 - feat(client)
Add handle429Backoff method for intelligent rate limit handling
Features:
- Added
handle429Backoff()method to SmartRequest class for automatic HTTP 429 handling - Respects
Retry-Afterheaders with support for both seconds and HTTP date formats - Configurable exponential backoff when no Retry-After header is present
- Added
RateLimitConfiginterface with customizable retry behavior - Optional callback for monitoring rate limit events
- Maximum wait time capping to prevent excessive delays
Improvements:
- Updated test endpoints to use more reliable services (jsonplaceholder, echo.zuplo.io)
- Added timeout parameter to test script for better CI/CD compatibility
Documentation:
- Added comprehensive rate limiting section to README with examples
- Documented all configuration options for handle429Backoff
2025-07-29 - 4.1.0 - feat(client)
Add missing options() method to SmartRequest client
Features:
- Added
options()method to SmartRequest class for setting arbitrary request options - Enables setting keepAlive and other platform-specific options via fluent API
- Added test coverage for keepAlive functionality
Documentation:
- Updated README with examples of using the
options()method - Added specific examples for enabling keepAlive connections
- Corrected all documentation to use
options()instead ofoption()
2025-07-28 - 4.0.0 - BREAKING CHANGE(core)
Complete architectural overhaul with cross-platform support
Breaking Changes:
- Renamed
SmartRequestClienttoSmartRequestfor simpler, cleaner API - Removed legacy API entirely (no more
/legacyimport path) - Major architectural refactoring:
- Added abstraction layer with
core_basecontaining abstract classes - Split implementations into
core_node(Node.js) andcore_fetch(browser) - Dynamic implementation selection based on environment
- Added abstraction layer with
- Response streaming API changes:
stream()now always returns web-styleReadableStream<Uint8Array>- Added
streamNode()for Node.js streams (throws error in browser)
- Unified type system with single
ICoreRequestOptionsinterface - Removed all "Abstract" prefixes from type names
Features:
- Full cross-platform support (Node.js and browsers)
- Automatic platform detection using @push.rocks/smartenv
- Consistent API across platforms with platform-specific capabilities
- Web Streams API support in both environments
- Better error messages for unsupported platform features
Documentation:
- Completely rewritten README with platform-specific examples
- Added architecture overview section
- Added migration guide from v2.x and v3.x
- Updated all examples to use the new
SmartRequestclass name
2025-07-27 - 3.0.0 - BREAKING CHANGE(core)
Major architectural refactoring with fetch-like API
Breaking Changes:
- Legacy API functions are now imported from
@push.rocks/smartrequest/legacyinstead of the main export - Modern API response objects now use fetch-like methods (
.json(),.text(),.arrayBuffer(),.stream()) instead of direct.bodyaccess - Renamed
responseType()method toaccept()in modern API - Removed automatic defaults:
- No default keepAlive (must be explicitly set)
- No default timeouts
- No automatic JSON parsing in core
- Complete internal architecture refactoring:
- Core module now always returns raw streams
- Response parsing happens in SmartResponse methods
- Legacy API is now just an adapter over the core module
Features:
- New fetch-like response API with single-use body consumption
- Better TypeScript support and type safety
- Cleaner separation of concerns between request and response
- More predictable behavior aligned with fetch API standards
Documentation:
- Updated all examples to show correct import paths
- Added comprehensive examples for the new response API
- Enhanced migration guide
2025-04-03 - 2.1.0 - feat(docs)
Enhance documentation and tests with modern API usage examples and migration guide
- Updated README to include detailed examples for the modern fluent API, covering GET, POST, headers, query, timeout, retries, and pagination
- Added a migration guide comparing the legacy API and modern API usage
- Improved installation instructions with npm, pnpm, and yarn examples
- Added and updated test files for both legacy and modern API functionalities
- Minor formatting improvements in the code and documentation examples
2024-11-06 - 2.0.23 - fix(core)
Enhance type safety for response in binary requests
- Updated the dependency versions in package.json to their latest versions.
- Improved type inference for the response body in getBinary method of smartrequest.binaryrest.ts.
- Introduced generic typing to IExtendedIncomingMessage interface for better type safety.
2024-05-29 - 2.0.22 - Documentation
update description
2024-04-01 - 2.0.21 - Configuration
Updated configuration files
- Updated
tsconfig - Updated
npmextra.json: githost
2023-07-10 - 2.0.15 - Structure
Refactored the organization structure
- Switched to a new organization scheme
2022-07-29 - 1.1.57 to 2.0.0 - Major Update
Significant changes and improvements leading to a major version update
- BREAKING CHANGE: Switched the core to use ECMAScript modules (ESM)
2018-08-14 - 1.1.12 to 1.1.13 - Functional Enhancements
Enhanced request capabilities and removed unnecessary dependencies
- Fixed request module to allow sending strings
- Removed CI dependencies
2018-07-19 - 1.1.1 to 1.1.11 - Various Fixes and Improvements
Improvements and fixes across various components
- Added formData capability
- Corrected path resolution to use current working directory (CWD)
- Improved formData handling
- Included correct headers
- Updated request ending method
2018-06-19 - 1.0.14 - Structural Fix
Resolved conflicts with file extensions
- Changed
.json.tsto.jsonrest.tsto avoid conflicts
2018-06-13 - 1.0.8 to 1.0.10 - Core Updates
Ensured binary handling compliance
- Enhanced core to uphold latest standards
- Correct binary file handling response
- Fix for handling and returning binary responses
2017-06-09 - 1.0.4 to 1.0.6 - Infrastructure and Type Improvements
Types and infrastructure updates
- Improved types
- Removed need for content type on post requests
- Updated for new infrastructure