feat(core): Add Bun and Deno runtime support, unify core loader, unix-socket support and cross-runtime streaming/tests

This commit is contained in:
2025-11-16 22:50:19 +00:00
parent 32332309dc
commit 6211acd60b
22 changed files with 1447 additions and 92 deletions
+173 -33
View File
@@ -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.