Compare commits

..

12 Commits

Author SHA1 Message Date
jkunz 735440fd11 v5.0.3
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-05-01 16:49:59 +00:00
jkunz 47bd335f4f fix(core_node): support agentkeepalive default export fallback and bump dependency to ^4.6.0 2026-05-01 16:49:59 +00:00
jkunz 8622ee78d1 v5.0.2
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-05-01 16:46:03 +00:00
jkunz c08e8b9132 fix(core-node): update agentkeepalive import usage and align package metadata with smartconfig 2026-05-01 16:46:03 +00:00
jkunz 980675ea05 v5.0.1
Default (tags) / security (push) Successful in 4s
Default (tags) / test (push) Failing after 48s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-11-17 14:37:09 +00:00
jkunz 15819d8a23 fix(test): Enable --logfile in test script and bump @git.zone/tstest to ^2.8.2 2025-11-17 14:37:08 +00:00
jkunz bc71d2e5a8 v5.0.0
Default (tags) / security (push) Successful in 14s
Default (tags) / test (push) Failing after 1m12s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-11-17 14:18:58 +00:00
jkunz 0cf48b3688 BREAKING CHANGE(client/streaming): Unify streaming APIs: remove raw()/streamNode() and standardize on web ReadableStream across runtimes 2025-11-17 14:18:58 +00:00
jkunz 1305b92ebe v4.4.2
Default (tags) / security (push) Successful in 5s
Default (tags) / test (push) Failing after 1m41s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-11-16 23:41:24 +00:00
jkunz 8b52ca1021 fix(core_base/request): Strip unix: prefix when parsing unix socket URLs so socketPath is a clean filesystem path 2025-11-16 23:41:24 +00:00
jkunz e14800f077 v4.4.1
Default (tags) / security (push) Successful in 13s
Default (tags) / test (push) Failing after 35s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-11-16 23:22:58 +00:00
jkunz 9f3503704b fix(core_node): Fix unix socket URL parsing and handling in CoreRequest 2025-11-16 23:22:58 +00:00
24 changed files with 1467 additions and 1595 deletions
+49
View File
@@ -0,0 +1,49 @@
{
"@git.zone/npmts": {
"coverageTreshold": 50
},
"@ship.zone/szci": {
"npmGlobalTools": [],
"npmRegistryUrl": "registry.npmjs.org"
},
"@git.zone/cli": {
"projectType": "npm",
"module": {
"githost": "code.foss.global",
"gitscope": "push.rocks",
"gitrepo": "smartrequest",
"shortDescription": "modern HTTP request utilities",
"description": "A module for modern HTTP/HTTPS requests with support for form data, file uploads, JSON, binary data, streams, and more.",
"npmPackagename": "@push.rocks/smartrequest",
"license": "MIT",
"keywords": [
"HTTP",
"HTTPS",
"request library",
"form data",
"file uploads",
"JSON",
"binary data",
"streams",
"keepAlive",
"TypeScript",
"modern web requests",
"drop-in replacement",
"Bun",
"Deno",
"Node.js",
"unix sockets"
]
},
"release": {
"registries": [
"https://verdaccio.lossless.digital",
"https://registry.npmjs.org"
],
"accessLevel": "public"
}
},
"@git.zone/tsdoc": {
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**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.\n\n### Trademarks\n\nThis 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.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy 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.\n"
}
}
+44
View File
@@ -1,5 +1,49 @@
# Changelog
## 2026-05-01 - 5.0.3 - fix(core_node)
support agentkeepalive default export fallback and bump dependency to ^4.6.0
- Adds a fallback to use the default agentkeepalive export when HttpAgent is not exposed directly.
- Updates agentkeepalive from ^4.5.0 to ^4.6.0 to align with the compatibility fix.
## 2026-05-01 - 5.0.2 - fix(core-node)
update agentkeepalive import usage and align package metadata with smartconfig
- Switches the Node plugin integration to use the default agentkeepalive export and map HttpAgent/HttpsAgent from it.
- Adds .smartconfig.json and includes it together with the license file in the published package.
- Updates npmextra.json to the new scoped configuration keys and expands project release metadata and keywords.
- Adjusts package metadata to include the author email address.
## 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
Generated
+219 -220
View File
File diff suppressed because it is too large Load Diff
+19
View File
@@ -0,0 +1,19 @@
Copyright (c) 2026 Task Venture Capital GmbH <hello@task.vc>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+18 -6
View File
@@ -1,17 +1,18 @@
{
"npmts": {
"@git.zone/npmts": {
"coverageTreshold": 50
},
"npmci": {
"@ship.zone/szci": {
"npmGlobalTools": [],
"npmAccessLevel": "public"
"npmRegistryUrl": "registry.npmjs.org"
},
"gitzone": {
"@git.zone/cli": {
"projectType": "npm",
"module": {
"githost": "code.foss.global",
"gitscope": "push.rocks",
"gitrepo": "smartrequest",
"shortDescription": "modern HTTP request utilities",
"description": "A module for modern HTTP/HTTPS requests with support for form data, file uploads, JSON, binary data, streams, and more.",
"npmPackagename": "@push.rocks/smartrequest",
"license": "MIT",
@@ -27,11 +28,22 @@
"keepAlive",
"TypeScript",
"modern web requests",
"drop-in replacement"
"drop-in replacement",
"Bun",
"Deno",
"Node.js",
"unix sockets"
]
},
"release": {
"registries": [
"https://verdaccio.lossless.digital",
"https://registry.npmjs.org"
],
"accessLevel": "public"
}
},
"tsdoc": {
"@git.zone/tsdoc": {
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**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.\n\n### Trademarks\n\nThis 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.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy 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.\n"
}
}
+10 -8
View File
@@ -1,6 +1,6 @@
{
"name": "@push.rocks/smartrequest",
"version": "4.4.0",
"version": "5.0.3",
"private": false,
"description": "A module for modern HTTP/HTTPS requests with support for form data, file uploads, JSON, binary data, streams, and more.",
"exports": {
@@ -12,7 +12,7 @@
},
"type": "module",
"scripts": {
"test": "(tstest test/ --verbose --timeout 120)",
"test": "(tstest test/ --verbose --timeout 120 --logfile)",
"build": "(tsbuild --web)",
"buildDocs": "tsdoc"
},
@@ -38,7 +38,7 @@
"Node.js",
"unix sockets"
],
"author": "Task Venture Capital GmbH",
"author": "Task Venture Capital GmbH <hello@task.vc>",
"license": "MIT",
"bugs": {
"url": "https://code.foss.global/push.rocks/smartrequest/issues"
@@ -49,13 +49,13 @@
"@push.rocks/smartpath": "^6.0.0",
"@push.rocks/smartpromise": "^4.0.4",
"@push.rocks/smarturl": "^3.1.0",
"agentkeepalive": "^4.5.0",
"form-data": "^4.0.4"
"agentkeepalive": "^4.6.0",
"form-data": "^4.0.5"
},
"devDependencies": {
"@git.zone/tsbuild": "^2.7.1",
"@git.zone/tsrun": "^1.6.2",
"@git.zone/tstest": "^2.7.0",
"@git.zone/tsbuild": "^3.1.0",
"@git.zone/tsrun": "^2.0.0",
"@git.zone/tstest": "^2.8.2",
"@types/node": "^22.9.0"
},
"files": [
@@ -67,6 +67,8 @@
"dist_ts_web/**/*",
"assets/**/*",
"cli.js",
".smartconfig.json",
"license",
"npmextra.json",
"readme.md"
],
+871 -1212
View File
File diff suppressed because it is too large Load Diff
+149 -34
View File
@@ -182,11 +182,11 @@ async function downloadImage(url: string) {
return Buffer.from(buffer); // Convert ArrayBuffer to Buffer if needed
}
// Streaming response (Web Streams API)
// 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 in both Node.js and browsers)
// Get a web-style ReadableStream (works everywhere)
const stream = response.stream();
if (stream) {
@@ -204,12 +204,14 @@ async function streamLargeFile(url: string) {
}
}
// Node.js specific stream (only in Node.js environment)
// Convert to Node.js stream if needed (Node.js only)
async function streamWithNodeApi(url: string) {
const response = await SmartRequest.create().url(url).get();
// Only available in Node.js, throws error in browser/Bun/Deno
const nodeStream = response.streamNode();
// 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`);
@@ -230,8 +232,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/Bun/Deno)
- `raw(): Response | http.IncomingMessage` - Get the underlying platform response
- `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.
@@ -368,24 +369,7 @@ async function streamData(dataSource: Readable) {
return await response.json();
}
// Advanced: Full control over request streaming (Node.js only)
async function customStreaming() {
const response = await SmartRequest.create()
.url('https://api.example.com/stream')
.raw((request) => {
// 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)
// Send Uint8Array (works everywhere)
async function uploadBinaryData() {
const data = new Uint8Array([72, 101, 108, 108, 111]); // "Hello"
@@ -411,11 +395,6 @@ async function uploadBinaryData() {
- ✅ 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, Bun, or Deno
- Use for advanced scenarios like chunked transfer encoding
These methods are particularly useful for:
- Uploading large files without loading them into memory
- Streaming real-time data to servers
@@ -687,13 +666,12 @@ const response = await SmartRequest.create()
})
.get();
// Bun uses web streams - streamNode() throws an error
// 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
// streamNode() is not available - throws error directing you to use stream()
```
### Deno-Specific Options
@@ -716,13 +694,12 @@ const response = await SmartRequest.create()
// Remember to clean up clients when done
client.close();
// Deno uses web streams - streamNode() throws an error
// 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
// streamNode() is not available - throws error directing you to use stream()
```
## Complete Example: Building a REST API Client
@@ -838,6 +815,144 @@ async function fetchWithErrorHandling(url: string) {
## 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**
1. **`.streamNode()` Method Removed**
- The `.streamNode()` method has been removed from all response objects
- Use the cross-platform `.stream()` method instead, which returns a web `ReadableStream<Uint8Array>`
- For Node.js users who need Node.js streams, convert using `Readable.fromWeb()`
```typescript
// ❌ 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
```
2. **Request `.raw()` Method Removed**
- The `.raw(streamFunc)` method has been removed from the SmartRequest client
- Use `.stream()` with a web `ReadableStream` instead for request body streaming
- Node.js users can create web streams from Node.js streams using `Readable.toWeb()`
```typescript
// ❌ 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();
```
3. **Response `.raw()` Method Preserved**
- The `response.raw()` method is still available for accessing platform-specific response objects
- Returns `http.IncomingMessage` in Node.js or `Response` in other runtimes
- Use for advanced scenarios requiring access to raw platform objects
```typescript
// ✅ Still works in v5.x
const response = await SmartRequest.create().url(url).get();
const rawResponse = response.raw(); // http.IncomingMessage or Response
```
#### **Migration Guide**
**For Response Streaming:**
```typescript
// 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:
```typescript
// ✅ 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:
+51 -10
View File
@@ -1,27 +1,68 @@
import { tap, expect } from '@git.zone/tstest/tapbundle';
import { SmartRequest } from '../ts/index.js';
import { Readable } from 'stream';
tap.test('should have streamNode() method available', async () => {
tap.test('should have stream() method that returns web ReadableStream', async () => {
const response = await SmartRequest.create()
.url('https://httpbin.org/get')
.get();
// Verify streamNode() method exists
expect(response.streamNode).toBeDefined();
expect(typeof response.streamNode).toEqual('function');
// In Node.js, it should return a stream
const nodeStream = response.streamNode();
// Verify stream() method exists
expect(response.stream).toBeDefined();
expect(typeof response.stream).toEqual('function');
// Get web stream
const webStream = response.stream();
expect(webStream).toBeDefined();
// Verify it's a web ReadableStream
expect(typeof webStream.getReader).toEqual('function');
expect(typeof webStream.cancel).toEqual('function');
// Convert to Node.js stream using Readable.fromWeb()
// Known TypeScript limitation: @types/node ReadableStream differs from web ReadableStream
const nodeStream = Readable.fromWeb(webStream as any);
expect(nodeStream).toBeDefined();
// Verify it's a Node.js readable stream
expect(typeof nodeStream.pipe).toEqual('function');
expect(typeof nodeStream.on).toEqual('function');
// Consume the stream to avoid hanging
nodeStream.resume();
});
tap.test('should convert web stream to Node.js stream correctly', async () => {
const response = await SmartRequest.create()
.url('https://httpbin.org/get')
.get();
const webStream = response.stream();
const nodeStream = Readable.fromWeb(webStream as any);
export default tap.start();
// Collect data from stream
const chunks: Buffer[] = [];
await new Promise<void>((resolve, reject) => {
nodeStream.on('data', (chunk) => {
chunks.push(chunk);
});
nodeStream.on('end', () => {
resolve();
});
nodeStream.on('error', reject);
});
// Verify we received data
const data = Buffer.concat(chunks);
expect(data.length).toBeGreaterThan(0);
// Verify it's valid JSON
const json = JSON.parse(data.toString('utf-8'));
expect(json).toBeDefined();
expect(json.url).toEqual('https://httpbin.org/get');
});
export default tap.start();
+1 -1
View File
@@ -25,7 +25,7 @@ tap.test('bun: should detect unix socket URLs correctly', async () => {
tap.test('bun: should parse unix socket URLs correctly', async () => {
const result = CoreRequest.parseUnixSocketUrl('unix:/var/run/docker.sock:/v1.24/version');
expect(result.socketPath).toEqual('unix:/var/run/docker.sock');
expect(result.socketPath).toEqual('/var/run/docker.sock');
expect(result.path).toEqual('/v1.24/version');
});
+1 -1
View File
@@ -25,7 +25,7 @@ tap.test('deno: should detect unix socket URLs correctly', async () => {
tap.test('deno: should parse unix socket URLs correctly', async () => {
const result = CoreRequest.parseUnixSocketUrl('unix:/var/run/docker.sock:/v1.24/version');
expect(result.socketPath).toEqual('unix:/var/run/docker.sock');
expect(result.socketPath).toEqual('/var/run/docker.sock');
expect(result.path).toEqual('/v1.24/version');
});
+1 -1
View File
@@ -26,7 +26,7 @@ tap.test('node: should detect unix socket URLs correctly', async () => {
tap.test('node: should parse unix socket URLs correctly', async () => {
const result = CoreRequest.parseUnixSocketUrl('unix:/var/run/docker.sock:/v1.24/version');
expect(result.socketPath).toEqual('unix:/var/run/docker.sock');
expect(result.socketPath).toEqual('/var/run/docker.sock');
expect(result.path).toEqual('/v1.24/version');
});
+1 -1
View File
@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@push.rocks/smartrequest',
version: '4.4.0',
version: '5.0.3',
description: 'A module for modern HTTP/HTTPS requests with support for form data, file uploads, JSON, binary data, streams, and more.'
}
+3 -19
View File
@@ -8,7 +8,6 @@ import type {
ResponseType,
FormField,
RateLimitConfig,
RawStreamFunction,
} from './types/common.js';
import {
type TPaginationConfig,
@@ -142,12 +141,12 @@ export class SmartRequest<T = any> {
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 as any).pipe === 'function') {
// For Node.js streams, we need to use a custom approach
@@ -157,18 +156,7 @@ export class SmartRequest<T = any> {
// For web ReadableStream, pass directly
this._options.requestBody = stream;
}
return this;
}
/**
* Provide a custom function to handle raw request streaming
* This gives full control over the request body streaming
* Note: Only works in Node.js environment, not supported in browsers
*/
raw(streamFunc: RawStreamFunction): this {
// Store the raw streaming function to be used later
(this._options as any).__rawStreamFunc = streamFunc;
return this;
}
@@ -440,7 +428,7 @@ export class SmartRequest<T = any> {
// Main retry loop
for (let attempt = 0; attempt <= this._retries; attempt++) {
try {
// Check if we have a Node.js stream or raw function that needs special handling
// Check if we have a Node.js stream that needs special handling
let requestDataFunc = null;
if ((this._options as any).__nodeStream) {
const nodeStream = (this._options as any).__nodeStream;
@@ -449,16 +437,12 @@ export class SmartRequest<T = any> {
};
// Don't delete __nodeStream yet - let CoreRequest implementations handle it
// Node.js will use requestDataFunc, Bun/Deno will convert the stream
} else if ((this._options as any).__rawStreamFunc) {
requestDataFunc = (this._options as any).__rawStreamFunc;
// Don't delete __rawStreamFunc yet - let CoreRequest implementations handle it
}
const request = new CoreRequest(this._url, this._options as any, requestDataFunc);
// Clean up temporary properties after CoreRequest has been created
delete (this._options as any).__nodeStream;
delete (this._options as any).__rawStreamFunc;
const response = (await request.fire()) as ICoreResponse<R>;
// Check for 429 status if rate limit handling is enabled
-6
View File
@@ -66,9 +66,3 @@ export interface RateLimitConfig {
backoffFactor?: number; // Exponential backoff factor (default: 2)
onRateLimit?: (attempt: number, waitTime: number) => void; // Callback for rate limit events
}
/**
* Raw streaming function for advanced request body control
* Note: The request parameter type depends on the environment (Node.js ClientRequest or fetch Request)
*/
export type RawStreamFunction = (request: any) => void;
+19 -1
View File
@@ -17,10 +17,28 @@ export abstract class CoreRequest<
/**
* Parses socket path and route from unix socket URL
* Handles both full URLs (http://unix:/path/to/socket:/route) and pre-stripped paths (unix:/path/to/socket:/route)
* Returns clean file system path for socketPath (e.g., /var/run/docker.sock)
*/
static parseUnixSocketUrl(url: string): { socketPath: string; path: string } {
// Strip http:// or https:// prefix if present
// This makes the method work with both full URLs and pre-stripped paths
let cleanUrl = url;
if (cleanUrl.startsWith('http://')) {
cleanUrl = cleanUrl.substring('http://'.length);
} else if (cleanUrl.startsWith('https://')) {
cleanUrl = cleanUrl.substring('https://'.length);
}
// Strip unix: prefix if present to get clean file system path
if (cleanUrl.startsWith('unix:')) {
cleanUrl = cleanUrl.substring('unix:'.length);
}
// Parse the socket path and HTTP path
// Format: /path/to/socket:/route/path
const parseRegex = /(.*):(.*)/;
const result = parseRegex.exec(url);
const result = parseRegex.exec(cleanUrl);
return {
socketPath: result[1],
path: result[2],
-5
View File
@@ -42,9 +42,4 @@ export abstract class CoreResponse<T = any> implements types.ICoreResponse<T> {
* Get response as a web-style ReadableStream
*/
abstract stream(): ReadableStream<Uint8Array> | null;
/**
* Get response as a Node.js stream (throws in browser)
*/
abstract streamNode(): NodeJS.ReadableStream | never;
}
-1
View File
@@ -86,5 +86,4 @@ export interface ICoreResponse<T = any> {
text(): Promise<string>;
arrayBuffer(): Promise<ArrayBuffer>;
stream(): ReadableStream<Uint8Array> | null; // Always returns web-style stream
streamNode(): NodeJS.ReadableStream | never; // Returns Node.js stream or throws in browser
}
-14
View File
@@ -72,20 +72,6 @@ export class CoreResponse<T = any>
return this.response.body;
}
/**
* Get response as a Node.js-style stream
* Bun supports Node.js streams, so we can provide this functionality
*
* Note: In Bun, you may also be able to use the web stream directly with stream() method
*/
streamNode(): never {
// Bun primarily uses web streams and has excellent compatibility
// For most use cases, use stream() which returns a standard ReadableStream
throw new Error(
'streamNode() is not available in Bun environment. Use stream() for web-style ReadableStream, which Bun fully supports.',
);
}
/**
* Get the raw Response object
*/
-10
View File
@@ -72,16 +72,6 @@ export class CoreResponse<T = any>
return this.response.body;
}
/**
* Node.js stream method - not available in Deno's standard mode
* Throws an error as Deno uses web-standard ReadableStream
*/
streamNode(): never {
throw new Error(
'streamNode() is not available in Deno environment. Use stream() for web-style ReadableStream.',
);
}
/**
* Get the raw Response object
*/
-9
View File
@@ -72,15 +72,6 @@ export class CoreResponse<T = any>
return this.response.body;
}
/**
* Node.js stream method - not available in browser
*/
streamNode(): never {
throw new Error(
'streamNode() is not available in browser/fetch environment. Use stream() for web-style ReadableStream.',
);
}
/**
* Get the raw Response object
*/
+5 -2
View File
@@ -14,8 +14,11 @@ import * as smarturl from '@push.rocks/smarturl';
export { smartpromise, smarturl };
// third party scope
import { HttpAgent, HttpsAgent } from 'agentkeepalive';
const agentkeepalive = { HttpAgent, HttpsAgent };
import AgentKeepAlive from 'agentkeepalive';
const agentkeepalive = {
HttpAgent: AgentKeepAlive.HttpAgent ?? AgentKeepAlive,
HttpsAgent: AgentKeepAlive.HttpsAgent,
};
import formData from 'form-data';
export { agentkeepalive, formData };
+1 -3
View File
@@ -86,9 +86,7 @@ export class CoreRequest extends AbstractCoreRequest<
// Handle unix socket URLs
if (CoreRequest.isUnixSocket(this.url)) {
const { socketPath, path } = CoreRequest.parseUnixSocketUrl(
this.options.path,
);
const { socketPath, path } = CoreRequest.parseUnixSocketUrl(this.url);
this.options.socketPath = socketPath;
this.options.path = path;
}
+5 -31
View File
@@ -129,41 +129,15 @@ export class CoreResponse<T = any>
stream(): ReadableStream<Uint8Array> | null {
this.ensureNotConsumed();
// Convert Node.js stream to web stream
// In Node.js 16.5+ we can use Readable.toWeb()
// Convert Node.js stream to web stream using Readable.toWeb()
// This creates a proper Node.js-compatible web stream that works with Readable.fromWeb()
if (this.incomingMessage.readableEnded || this.incomingMessage.destroyed) {
return null;
}
// Create a web ReadableStream from the Node.js stream
const nodeStream = this.incomingMessage;
return new ReadableStream<Uint8Array>({
start(controller) {
nodeStream.on('data', (chunk) => {
controller.enqueue(new Uint8Array(chunk));
});
nodeStream.on('end', () => {
controller.close();
});
nodeStream.on('error', (err) => {
controller.error(err);
});
},
cancel() {
nodeStream.destroy();
},
});
}
/**
* Get response as a Node.js readable stream
*/
streamNode(): NodeJS.ReadableStream {
this.ensureNotConsumed();
return this.incomingMessage;
// Use Readable.toWeb() to convert Node.js stream to web stream (Node.js 16.5+)
// The returned type is automatically Node.js ReadableStream which is compatible with Readable.fromWeb()
return plugins.stream.Readable.toWeb(this.incomingMessage) as any;
}
/**