feat(core): Add Bun and Deno runtime support, unify core loader, unix-socket support and cross-runtime streaming/tests
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# @push.rocks/smartrequest
|
||||
|
||||
A modern, cross-platform HTTP/HTTPS request library for Node.js and browsers with a unified API, supporting form data, file uploads, JSON, binary data, streams, and unix sockets.
|
||||
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
|
||||
|
||||
@@ -18,26 +18,30 @@ yarn add @push.rocks/smartrequest
|
||||
## Key Features
|
||||
|
||||
- 🚀 **Modern Fetch-like API** - Familiar response methods (`.json()`, `.text()`, `.arrayBuffer()`, `.stream()`)
|
||||
- 🌐 **Cross-Platform** - Works in both Node.js and browsers with a unified API
|
||||
- 🔌 **Unix Socket Support** - Connect to local services like Docker (Node.js only)
|
||||
- 🌐 **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, and more
|
||||
- 🔧 **Highly Configurable** - Timeouts, retries, headers, rate limiting, and more
|
||||
|
||||
## Architecture
|
||||
|
||||
SmartRequest v3.0 features a multi-layer architecture that provides consistent behavior across platforms:
|
||||
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
|
||||
- **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 `unix` option
|
||||
- **Core Deno** - Deno implementation using fetch with unix socket support via HttpClient proxy
|
||||
- **Core Fetch** - Browser implementation using the Fetch API
|
||||
- **Core** - Dynamic implementation selection based on environment
|
||||
- **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.
|
||||
@@ -204,7 +208,7 @@ async function streamLargeFile(url: string) {
|
||||
async function streamWithNodeApi(url: string) {
|
||||
const response = await SmartRequest.create().url(url).get();
|
||||
|
||||
// Only available in Node.js, throws error in browser
|
||||
// Only available in Node.js, throws error in browser/Bun/Deno
|
||||
const nodeStream = response.streamNode();
|
||||
|
||||
nodeStream.on('data', (chunk) => {
|
||||
@@ -226,7 +230,7 @@ The response object provides these methods:
|
||||
- `text(): Promise<string>` - Get response as text
|
||||
- `arrayBuffer(): Promise<ArrayBuffer>` - Get response as ArrayBuffer
|
||||
- `stream(): ReadableStream<Uint8Array> | null` - Get web-style ReadableStream (cross-platform)
|
||||
- `streamNode(): NodeJS.ReadableStream` - Get Node.js stream (Node.js only, throws in browser)
|
||||
- `streamNode(): NodeJS.ReadableStream` - Get Node.js stream (Node.js only, throws in browser/Bun/Deno)
|
||||
- `raw(): Response | http.IncomingMessage` - Get the underlying platform response
|
||||
|
||||
Each body method can only be called once per response, similar to the fetch API.
|
||||
@@ -312,37 +316,55 @@ import { SmartRequest } from '@push.rocks/smartrequest';
|
||||
import * as fs from 'fs';
|
||||
import { Readable } from 'stream';
|
||||
|
||||
// Stream a Buffer directly
|
||||
// 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 a file using Node.js streams
|
||||
// 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
|
||||
// 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();
|
||||
}
|
||||
|
||||
@@ -354,24 +376,24 @@ async function customStreaming() {
|
||||
// Custom streaming logic - you have full control
|
||||
request.write('chunk1');
|
||||
request.write('chunk2');
|
||||
|
||||
|
||||
// Stream from another source
|
||||
someReadableStream.pipe(request);
|
||||
})
|
||||
.post();
|
||||
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
// Send Uint8Array (works in both Node.js and browser)
|
||||
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();
|
||||
}
|
||||
```
|
||||
@@ -379,19 +401,19 @@ async function uploadBinaryData() {
|
||||
#### Streaming Methods
|
||||
|
||||
- **`.buffer(data, contentType?)`** - Stream a Buffer or Uint8Array directly
|
||||
- `data`: Buffer (Node.js) or Uint8Array (both platforms) to send
|
||||
- `data`: Buffer (Node.js) or Uint8Array (cross-platform) to send
|
||||
- `contentType`: Optional content type (defaults to 'application/octet-stream')
|
||||
- ✅ Works in both Node.js and browsers
|
||||
- ✅ Works everywhere (Node.js, Bun, Deno, browsers)
|
||||
|
||||
- **`.stream(stream, contentType?)`** - Stream from ReadableStream
|
||||
- `stream`: Web ReadableStream (both platforms) or Node.js stream (Node.js only)
|
||||
- **`.stream(stream, contentType?)`** - Stream from ReadableStream or Node.js stream
|
||||
- `stream`: Web ReadableStream (cross-platform) or Node.js stream (Node.js only)
|
||||
- `contentType`: Optional content type
|
||||
- ✅ Web ReadableStream works in both Node.js and browsers
|
||||
- ⚠️ Node.js streams only work in Node.js environment
|
||||
- ✅ 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)
|
||||
|
||||
- **`.raw(streamFunc)`** - Advanced control over request streaming
|
||||
- `streamFunc`: Function that receives the raw request object for custom streaming
|
||||
- ❌ **Node.js only** - not supported in browsers
|
||||
- ❌ **Node.js only** - not supported in browsers, Bun, or Deno
|
||||
- Use for advanced scenarios like chunked transfer encoding
|
||||
|
||||
These methods are particularly useful for:
|
||||
@@ -400,12 +422,14 @@ These methods are particularly useful for:
|
||||
- Proxying data between services
|
||||
- Implementing chunked transfer encoding
|
||||
|
||||
### Unix Socket Support (Node.js only)
|
||||
### Unix Socket Support (Node.js, Bun, and Deno)
|
||||
|
||||
SmartRequest supports unix sockets across all server-side runtimes with a unified API:
|
||||
|
||||
```typescript
|
||||
import { SmartRequest } from '@push.rocks/smartrequest';
|
||||
|
||||
// Connect to a service via Unix socket
|
||||
// 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')
|
||||
@@ -413,6 +437,57 @@ async function queryViaUnixSocket() {
|
||||
|
||||
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:**
|
||||
```typescript
|
||||
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:**
|
||||
```typescript
|
||||
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:**
|
||||
```typescript
|
||||
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
|
||||
@@ -599,12 +674,63 @@ const response = await SmartRequest.create()
|
||||
.get();
|
||||
```
|
||||
|
||||
### Bun-Specific Options
|
||||
|
||||
When running in Bun, you can use Bun-specific options:
|
||||
|
||||
```typescript
|
||||
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 - streamNode() throws an error
|
||||
const streamResponse = await SmartRequest.create()
|
||||
.url('https://api.example.com/data')
|
||||
.get();
|
||||
|
||||
const webStream = streamResponse.stream(); // ✅ Use web streams in Bun
|
||||
// streamNode() is not available - throws error directing you to use stream()
|
||||
```
|
||||
|
||||
### Deno-Specific Options
|
||||
|
||||
When running in Deno, you can use Deno-specific options:
|
||||
|
||||
```typescript
|
||||
// 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 - streamNode() throws an error
|
||||
const streamResponse = await SmartRequest.create()
|
||||
.url('https://api.example.com/data')
|
||||
.get();
|
||||
|
||||
const webStream = streamResponse.stream(); // ✅ Use web streams in Deno
|
||||
// streamNode() is not available - throws error directing you to use stream()
|
||||
```
|
||||
|
||||
## Complete Example: Building a REST API Client
|
||||
|
||||
Here's a complete example of building a typed API client:
|
||||
|
||||
```typescript
|
||||
import { SmartRequest, type CoreResponse } from '@push.rocks/smartrequest';
|
||||
import { SmartRequest, type ICoreResponse } from '@push.rocks/smartrequest';
|
||||
|
||||
interface User {
|
||||
id: number;
|
||||
@@ -644,6 +770,9 @@ class BlogApiClient {
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to delete post: ${response.statusText}`);
|
||||
}
|
||||
|
||||
// Consume the body
|
||||
await response.text();
|
||||
}
|
||||
|
||||
async getAllPosts(userId?: number): Promise<Post[]> {
|
||||
@@ -707,9 +836,20 @@ async function fetchWithErrorHandling(url: string) {
|
||||
}
|
||||
```
|
||||
|
||||
## Migrating from v2.x to v3.x
|
||||
## Migrating from Earlier Versions
|
||||
|
||||
Version 3.0 brings significant architectural improvements and a more consistent API:
|
||||
### From v3.x to v4.x
|
||||
|
||||
Version 4.0 adds comprehensive cross-platform support:
|
||||
|
||||
1. **Multi-Runtime Support**: Now works natively in Node.js, Bun, Deno, and browsers
|
||||
2. **Unix Sockets Everywhere**: Unix socket support added for Bun and Deno
|
||||
3. **Web Streams**: Full support for web ReadableStream across all platforms
|
||||
4. **Automatic Runtime Detection**: No configuration needed - works everywhere automatically
|
||||
|
||||
### From v2.x to v3.x
|
||||
|
||||
Version 3.0 brought significant architectural improvements:
|
||||
|
||||
1. **Legacy API Removed**: The function-based API (getJson, postJson, etc.) has been removed. Use SmartRequest instead.
|
||||
2. **Unified Response API**: All responses now use the same fetch-like interface regardless of platform.
|
||||
|
||||
Reference in New Issue
Block a user