BREAKING CHANGE(client/streaming): Unify streaming APIs: remove raw()/streamNode() and standardize on web ReadableStream across runtimes
This commit is contained in:
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartrequest',
|
||||
version: '4.4.2',
|
||||
version: '5.0.0',
|
||||
description: 'A module for modern HTTP/HTTPS requests with support for form data, file uploads, JSON, binary data, streams, and more.'
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user