BREAKING CHANGE(core): major architectural refactoring with cross-platform support and SmartRequest rename
This commit is contained in:
		
							
								
								
									
										29
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								changelog.md
									
									
									
									
									
								
							| @@ -1,5 +1,34 @@ | ||||
| # Changelog | ||||
|  | ||||
| ## 2025-07-28 - 4.0.0 - BREAKING CHANGE(core) | ||||
| Complete architectural overhaul with cross-platform support | ||||
|  | ||||
| **Breaking Changes:** | ||||
| - Renamed `SmartRequestClient` to `SmartRequest` for simpler, cleaner API | ||||
| - Removed legacy API entirely (no more `/legacy` import path) | ||||
| - Major architectural refactoring: | ||||
|   - Added abstraction layer with `core_base` containing abstract classes | ||||
|   - Split implementations into `core_node` (Node.js) and `core_fetch` (browser) | ||||
|   - Dynamic implementation selection based on environment | ||||
| - Response streaming API changes: | ||||
|   - `stream()` now always returns web-style `ReadableStream<Uint8Array>` | ||||
|   - Added `streamNode()` for Node.js streams (throws error in browser) | ||||
| - Unified type system with single `ICoreRequestOptions` interface | ||||
| - 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 `SmartRequest` class name | ||||
|  | ||||
| ## 2025-07-27 - 3.0.0 - BREAKING CHANGE(core) | ||||
| Major architectural refactoring with fetch-like API | ||||
|  | ||||
|   | ||||
							
								
								
									
										13
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,12 +1,12 @@ | ||||
| { | ||||
|   "name": "@push.rocks/smartrequest", | ||||
|   "version": "3.0.0", | ||||
|   "version": "4.0.0", | ||||
|   "private": false, | ||||
|   "description": "A module for modern HTTP/HTTPS requests with support for form data, file uploads, JSON, binary data, streams, and more.", | ||||
|   "exports": { | ||||
|     ".": "./dist_ts_web/index.js", | ||||
|     "./legacy": "./dist_ts/legacy/index.js", | ||||
|     "./fetch": "./dist_ts/core_fetch/index.js" | ||||
|     "./core_node": "./dist_ts/core_node/index.js", | ||||
|     "./core_fetch": "./dist_ts/core_fetch/index.js" | ||||
|   }, | ||||
|   "type": "module", | ||||
|   "scripts": { | ||||
| @@ -44,13 +44,12 @@ | ||||
|     "@push.rocks/smartpromise": "^4.0.4", | ||||
|     "@push.rocks/smarturl": "^3.1.0", | ||||
|     "agentkeepalive": "^4.5.0", | ||||
|     "form-data": "^4.0.1" | ||||
|     "form-data": "^4.0.4" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@git.zone/tsbuild": "^2.2.0", | ||||
|     "@git.zone/tsbuild": "^2.6.4", | ||||
|     "@git.zone/tsrun": "^1.3.3", | ||||
|     "@git.zone/tstest": "^1.0.90", | ||||
|     "@pushrocks/tapbundle": "^5.0.8", | ||||
|     "@git.zone/tstest": "^2.3.2", | ||||
|     "@types/node": "^22.9.0" | ||||
|   }, | ||||
|   "files": [ | ||||
|   | ||||
							
								
								
									
										4185
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4185
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -2,7 +2,7 @@ | ||||
|  | ||||
| ## Core Features | ||||
| - supports http | ||||
| - supports https | ||||
| - supports https   | ||||
| - supports unix socks | ||||
| - supports formData | ||||
| - supports file uploads | ||||
| @@ -11,44 +11,69 @@ | ||||
| - written in TypeScript | ||||
| - continuously updated | ||||
| - uses node native http and https modules | ||||
| - supports both Node.js and browser environments | ||||
| - used in modules like @push.rocks/smartproxy and @api.global/typedrequest | ||||
|  | ||||
| ## Architecture Overview (as of latest refactoring) | ||||
| - The project is now structured with a clean separation between core functionality and API layers | ||||
| - Core module (ts/core/) contains the essential HTTP request logic using Node.js http/https modules | ||||
| - **Core always returns raw streams** - no parsing or body collection happens in the core request function | ||||
| - Modern API (ts/modern/) provides a fluent, chainable interface with fetch-like Response objects | ||||
| - Legacy API is maintained through a thin adapter layer for backward compatibility | ||||
| ## Architecture Overview (as of v3.0.0 major refactoring) | ||||
| - The project now has a multi-layer architecture with platform abstraction | ||||
| - Base layer (ts/core_base/) contains abstract classes and unified types | ||||
| - Node.js implementation (ts/core_node/) uses native http/https modules | ||||
| - Fetch implementation (ts/core_fetch/) uses Fetch API for browser compatibility | ||||
| - Core module (ts/core/) dynamically selects the appropriate implementation based on environment | ||||
| - Client API (ts/client/) provides a fluent, chainable interface   | ||||
| - Legacy API has been completely removed in v3.0.0 | ||||
|  | ||||
| ## Key Components | ||||
|  | ||||
| ### Core Module (ts/core/) | ||||
| - `request.ts`: Core HTTP/HTTPS request logic with unix socket support and keep-alive agents | ||||
|   - `coreRequest()` always returns a raw Node.js IncomingMessage stream | ||||
|   - No response parsing or body collection happens here | ||||
| - `response.ts`: SmartResponse class providing fetch-like API | ||||
|   - Methods like `json()`, `text()`, `arrayBuffer()` handle all parsing and body collection | ||||
|   - Response body is streamed and collected only when these methods are called | ||||
| ### Core Base Module (ts/core_base/) | ||||
| - `request.ts`: Abstract CoreRequest class defining the request interface | ||||
| - `response.ts`: Abstract CoreResponse class with fetch-like API | ||||
|   - Defines `stream()` method that always returns web-style ReadableStream | ||||
|   - Body can only be consumed once (throws error on second attempt) | ||||
| - `types.ts`: Core TypeScript interfaces and types | ||||
| - `plugins.ts`: Centralized dependencies | ||||
| - `types.ts`: Unified TypeScript interfaces and types | ||||
|   - Single `ICoreRequestOptions` interface for all implementations | ||||
|   - Implementations handle unsupported options by throwing errors | ||||
|  | ||||
| ### Modern API | ||||
| - SmartRequestClient: Fluent API with method chaining | ||||
| - Returns SmartResponse objects with fetch-like methods | ||||
| ### Core Node Module (ts/core_node/) | ||||
| - `request.ts`: Node.js implementation using http/https modules | ||||
|   - Supports unix socket connections and keep-alive agents | ||||
|   - Converts Node.js specific options from unified interface | ||||
| - `response.ts`: Node.js CoreResponse implementation | ||||
|   - `stream()` method converts Node.js stream to web ReadableStream | ||||
|   - `streamNode()` method returns native Node.js stream | ||||
|   - Methods like `json()`, `text()`, `arrayBuffer()` handle parsing | ||||
|  | ||||
| ### Core Fetch Module (ts/core_fetch/) | ||||
| - `request.ts`: Fetch API implementation for browsers | ||||
|   - Throws errors for Node.js specific options (agent, socketPath) | ||||
|   - Native support for CORS, credentials, and other browser features | ||||
| - `response.ts`: Fetch-based CoreResponse implementation | ||||
|   - `stream()` returns native web ReadableStream from response.body | ||||
|   - `streamNode()` throws error explaining it's not available in browser | ||||
|  | ||||
| ### Core Module (ts/core/) | ||||
| - Dynamically loads appropriate implementation based on environment | ||||
| - Uses @push.rocks/smartenv for environment detection | ||||
| - Exports unified types from core_base | ||||
|  | ||||
| ### Client API (ts/client/) | ||||
| - SmartRequest: Fluent API with method chaining | ||||
| - Returns CoreResponse objects with fetch-like methods | ||||
| - Supports pagination, retries, timeouts, and various response types | ||||
|  | ||||
| ### Binary Request Handling | ||||
| - Binary requests are handled correctly when `responseType: 'binary'` is set | ||||
| - Response body is kept as Buffer without string conversion | ||||
| - No automatic transformations applied to binary data | ||||
| ### Stream Handling | ||||
| - `stream()` method always returns web-style ReadableStream<Uint8Array> | ||||
| - In Node.js, converts native streams to web streams | ||||
| - `streamNode()` available only in Node.js environment for native streams | ||||
| - Consistent API across platforms while preserving platform-specific capabilities | ||||
|  | ||||
| ### Legacy Compatibility | ||||
| - All legacy functions (getJson, postJson, etc.) are maintained through adapter.ts | ||||
| - Legacy API returns IExtendedIncomingMessage for backward compatibility | ||||
| - Modern API can be accessed alongside legacy API | ||||
| ### Binary Request Handling | ||||
| - Binary requests handled through ArrayBuffer API | ||||
| - Response body kept as Buffer/ArrayBuffer without string conversion | ||||
| - No automatic transformations applied to binary data | ||||
|  | ||||
| ## Testing | ||||
| - Use `pnpm test` to run all tests | ||||
| - Modern API tests use the new SmartResponse methods (response.json(), response.text()) | ||||
| - Legacy API tests continue to use the body property directly | ||||
| - Tests use @git.zone/tstest/tapbundle for assertions | ||||
| - Separate test files for Node.js (test.node.ts) and browser (test.browser.ts) | ||||
| - Browser tests run in headless Chromium via puppeteer | ||||
|   | ||||
							
								
								
									
										208
									
								
								readme.md
									
									
									
									
									
								
							
							
						
						
									
										208
									
								
								readme.md
									
									
									
									
									
								
							| @@ -1,12 +1,12 @@ | ||||
| # @push.rocks/smartrequest | ||||
| A modern HTTP/HTTPS request library for Node.js with a fetch-like API, supporting form data, file uploads, JSON, binary data, streams, and unix sockets. | ||||
| 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. | ||||
|  | ||||
| ## Install | ||||
| ```bash | ||||
| # Using npm | ||||
| npm install @push.rocks/smartrequest --save | ||||
|  | ||||
| # Using pnpm | ||||
| # Using pnpm   | ||||
| pnpm add @push.rocks/smartrequest | ||||
|  | ||||
| # Using yarn | ||||
| @@ -16,28 +16,38 @@ yarn add @push.rocks/smartrequest | ||||
| ## Key Features | ||||
|  | ||||
| - 🚀 **Modern Fetch-like API** - Familiar response methods (`.json()`, `.text()`, `.arrayBuffer()`, `.stream()`) | ||||
| - 🌐 **Unix Socket Support** - Connect to local services like Docker | ||||
| - 🌐 **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) | ||||
| - 📦 **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 | ||||
| - ⚡ **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** - Handle large files and real-time data | ||||
| - 📡 **Streaming Support** - Handle large files and real-time data | ||||
| - 🔧 **Highly Configurable** - Timeouts, retries, headers, and more | ||||
| - 🔄 **Legacy API Available** - For backward compatibility | ||||
|  | ||||
| ## Architecture | ||||
|  | ||||
| SmartRequest v3.0 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 Fetch** - Browser implementation using the Fetch API | ||||
| - **Core** - Dynamic implementation selection based on environment | ||||
| - **Client** - High-level fluent API for everyday use | ||||
|  | ||||
| ## Usage | ||||
|  | ||||
| `@push.rocks/smartrequest` provides a clean, type-safe API inspired by the native fetch API but with additional features needed for server-side applications. | ||||
| `@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 | ||||
|  | ||||
| ```typescript | ||||
| import { SmartRequestClient } from '@push.rocks/smartrequest'; | ||||
| import { SmartRequest } from '@push.rocks/smartrequest'; | ||||
|  | ||||
| // Simple GET request | ||||
| async function fetchUserData(userId: number) { | ||||
|   const response = await SmartRequestClient.create() | ||||
|   const response = await SmartRequest.create() | ||||
|     .url(`https://jsonplaceholder.typicode.com/users/${userId}`) | ||||
|     .get(); | ||||
|  | ||||
| @@ -48,7 +58,7 @@ async function fetchUserData(userId: number) { | ||||
|  | ||||
| // POST request with JSON body | ||||
| async function createPost(title: string, body: string, userId: number) { | ||||
|   const response = await SmartRequestClient.create() | ||||
|   const response = await SmartRequest.create() | ||||
|     .url('https://jsonplaceholder.typicode.com/posts') | ||||
|     .json({ title, body, userId }) | ||||
|     .post(); | ||||
| @@ -58,13 +68,34 @@ async function createPost(title: string, body: string, userId: number) { | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ### Direct Core API Usage | ||||
|  | ||||
| For advanced use cases, you can use the Core API directly: | ||||
|  | ||||
| ```typescript | ||||
| 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 | ||||
|  | ||||
| ```typescript | ||||
| import { SmartRequestClient } from '@push.rocks/smartrequest'; | ||||
| import { SmartRequest } from '@push.rocks/smartrequest'; | ||||
|  | ||||
| async function searchRepositories(query: string, perPage: number = 10) { | ||||
|   const response = await SmartRequestClient.create() | ||||
|   const response = await SmartRequest.create() | ||||
|     .url('https://api.github.com/search/repositories') | ||||
|     .header('Accept', 'application/vnd.github.v3+json') | ||||
|     .query({ | ||||
| @@ -81,10 +112,10 @@ async function searchRepositories(query: string, perPage: number = 10) { | ||||
| ### Handling Timeouts and Retries | ||||
|  | ||||
| ```typescript | ||||
| import { SmartRequestClient } from '@push.rocks/smartrequest'; | ||||
| import { SmartRequest } from '@push.rocks/smartrequest'; | ||||
|  | ||||
| async function fetchWithRetry(url: string) { | ||||
|   const response = await SmartRequestClient.create() | ||||
|   const response = await SmartRequest.create() | ||||
|     .url(url) | ||||
|     .timeout(5000) // 5 seconds timeout | ||||
|     .retry(3) // Retry up to 3 times on failure | ||||
| @@ -99,11 +130,11 @@ async function fetchWithRetry(url: string) { | ||||
| The API provides a fetch-like interface for handling different response types: | ||||
|  | ||||
| ```typescript | ||||
| import { SmartRequestClient } from '@push.rocks/smartrequest'; | ||||
| import { SmartRequest } from '@push.rocks/smartrequest'; | ||||
|  | ||||
| // JSON response (default) | ||||
| async function fetchJson(url: string) { | ||||
|   const response = await SmartRequestClient.create() | ||||
|   const response = await SmartRequest.create() | ||||
|     .url(url) | ||||
|     .get(); | ||||
|    | ||||
| @@ -112,7 +143,7 @@ async function fetchJson(url: string) { | ||||
|  | ||||
| // Text response | ||||
| async function fetchText(url: string) { | ||||
|   const response = await SmartRequestClient.create() | ||||
|   const response = await SmartRequest.create() | ||||
|     .url(url) | ||||
|     .get(); | ||||
|    | ||||
| @@ -121,7 +152,7 @@ async function fetchText(url: string) { | ||||
|  | ||||
| // Binary data | ||||
| async function downloadImage(url: string) { | ||||
|   const response = await SmartRequestClient.create() | ||||
|   const response = await SmartRequest.create() | ||||
|     .url(url) | ||||
|     .accept('binary') // Optional: hints to server we want binary | ||||
|     .get(); | ||||
| @@ -130,35 +161,60 @@ async function downloadImage(url: string) { | ||||
|   return Buffer.from(buffer); // Convert ArrayBuffer to Buffer if needed | ||||
| } | ||||
|  | ||||
| // Streaming response | ||||
| // Streaming response (Web Streams API) | ||||
| async function streamLargeFile(url: string) { | ||||
|   const response = await SmartRequestClient.create() | ||||
|   const response = await SmartRequest.create() | ||||
|     .url(url) | ||||
|     .get(); | ||||
|  | ||||
|   // Get the underlying Node.js stream | ||||
|   // Get a web-style ReadableStream (works in both Node.js and browsers) | ||||
|   const stream = response.stream(); | ||||
|    | ||||
|   stream.on('data', (chunk) => { | ||||
|   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(); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Node.js specific stream (only in Node.js environment) | ||||
| async function streamWithNodeApi(url: string) { | ||||
|   const response = await SmartRequest.create() | ||||
|     .url(url) | ||||
|     .get(); | ||||
|  | ||||
|   // Only available in Node.js, throws error in browser | ||||
|   const nodeStream = response.streamNode(); | ||||
|    | ||||
|   nodeStream.on('data', (chunk) => { | ||||
|     console.log(`Received ${chunk.length} bytes of data`); | ||||
|   }); | ||||
|  | ||||
|   return new Promise((resolve, reject) => { | ||||
|     stream.on('end', resolve); | ||||
|     stream.on('error', reject); | ||||
|     nodeStream.on('end', resolve); | ||||
|     nodeStream.on('error', reject); | ||||
|   }); | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ### Response Object Methods | ||||
|  | ||||
| The `SmartResponse` object provides these methods: | ||||
| The response object provides these methods: | ||||
|  | ||||
| - `json<T>(): Promise<T>` - Parse response as JSON | ||||
| - `text(): Promise<string>` - Get response as text | ||||
| - `arrayBuffer(): Promise<ArrayBuffer>` - Get response as ArrayBuffer | ||||
| - `stream(): NodeJS.ReadableStream` - Get the underlying Node.js stream | ||||
| - `raw(): http.IncomingMessage` - Get the raw http.IncomingMessage | ||||
| - `stream(): ReadableStream<Uint8Array> | null` - Get web-style ReadableStream (cross-platform) | ||||
| - `streamNode(): NodeJS.ReadableStream` - Get Node.js stream (Node.js only, throws in browser) | ||||
| - `raw(): Response | http.IncomingMessage` - Get the underlying platform response | ||||
|  | ||||
| Each body method can only be called once per response, similar to the fetch API. | ||||
|  | ||||
| @@ -167,7 +223,7 @@ Each body method can only be called once per response, similar to the fetch API. | ||||
| ### Form Data with File Uploads | ||||
|  | ||||
| ```typescript | ||||
| import { SmartRequestClient } from '@push.rocks/smartrequest'; | ||||
| import { SmartRequest } from '@push.rocks/smartrequest'; | ||||
| import * as fs from 'fs'; | ||||
|  | ||||
| async function uploadMultipleFiles(files: Array<{name: string, path: string}>) { | ||||
| @@ -178,7 +234,7 @@ async function uploadMultipleFiles(files: Array<{name: string, path: string}>) { | ||||
|     contentType: 'application/octet-stream' | ||||
|   })); | ||||
|  | ||||
|   const response = await SmartRequestClient.create() | ||||
|   const response = await SmartRequest.create() | ||||
|     .url('https://api.example.com/upload') | ||||
|     .formData(formFields) | ||||
|     .post(); | ||||
| @@ -187,14 +243,14 @@ async function uploadMultipleFiles(files: Array<{name: string, path: string}>) { | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ### Unix Socket Support | ||||
| ### Unix Socket Support (Node.js only) | ||||
|  | ||||
| ```typescript | ||||
| import { SmartRequestClient } from '@push.rocks/smartrequest'; | ||||
| import { SmartRequest } from '@push.rocks/smartrequest'; | ||||
|  | ||||
| // Connect to a service via Unix socket | ||||
| async function queryViaUnixSocket() { | ||||
|   const response = await SmartRequestClient.create() | ||||
|   const response = await SmartRequest.create() | ||||
|     .url('http://unix:/var/run/docker.sock:/v1.24/containers/json') | ||||
|     .get(); | ||||
|    | ||||
| @@ -207,11 +263,11 @@ async function queryViaUnixSocket() { | ||||
| The library includes built-in support for various pagination strategies: | ||||
|  | ||||
| ```typescript | ||||
| import { SmartRequestClient } from '@push.rocks/smartrequest'; | ||||
| import { SmartRequest } from '@push.rocks/smartrequest'; | ||||
|  | ||||
| // Offset-based pagination (page & limit) | ||||
| async function fetchAllUsers() { | ||||
|   const client = SmartRequestClient.create() | ||||
|   const client = SmartRequest.create() | ||||
|     .url('https://api.example.com/users') | ||||
|     .withOffsetPagination({ | ||||
|       pageParam: 'page', | ||||
| @@ -239,7 +295,7 @@ async function fetchAllUsers() { | ||||
|  | ||||
| // Cursor-based pagination | ||||
| async function fetchAllPosts() { | ||||
|   const allPosts = await SmartRequestClient.create() | ||||
|   const allPosts = await SmartRequest.create() | ||||
|     .url('https://api.example.com/posts') | ||||
|     .withCursorPagination({ | ||||
|       cursorParam: 'cursor', | ||||
| @@ -253,7 +309,7 @@ async function fetchAllPosts() { | ||||
|  | ||||
| // Link header-based pagination (GitHub API style) | ||||
| async function fetchAllIssues(repo: string) { | ||||
|   const paginatedResponse = await SmartRequestClient.create() | ||||
|   const paginatedResponse = await SmartRequest.create() | ||||
|     .url(`https://api.github.com/repos/${repo}/issues`) | ||||
|     .header('Accept', 'application/vnd.github.v3+json') | ||||
|     .withLinkPagination() | ||||
| @@ -263,17 +319,17 @@ async function fetchAllIssues(repo: string) { | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ### Keep-Alive Connections | ||||
| ### Keep-Alive Connections (Node.js) | ||||
|  | ||||
| ```typescript | ||||
| import { SmartRequestClient } from '@push.rocks/smartrequest'; | ||||
| import { SmartRequest } from '@push.rocks/smartrequest'; | ||||
|  | ||||
| // Enable keep-alive for better performance with multiple requests | ||||
| async function performMultipleRequests() { | ||||
|   const client = SmartRequestClient.create() | ||||
|   const client = SmartRequest.create() | ||||
|     .header('Connection', 'keep-alive'); | ||||
|  | ||||
|   // Requests will reuse the same connection | ||||
|   // Requests will reuse the same connection in Node.js | ||||
|   const results = await Promise.all([ | ||||
|     client.url('https://api.example.com/endpoint1').get(), | ||||
|     client.url('https://api.example.com/endpoint2').get(), | ||||
| @@ -284,12 +340,46 @@ async function performMultipleRequests() { | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ## Platform-Specific Features | ||||
|  | ||||
| ### Browser-Specific Options | ||||
|  | ||||
| When running in a browser, you can use browser-specific fetch options: | ||||
|  | ||||
| ```typescript | ||||
| const response = await SmartRequest.create() | ||||
|   .url('https://api.example.com/data') | ||||
|   .option({ | ||||
|     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: | ||||
|  | ||||
| ```typescript | ||||
| import { Agent } from 'https'; | ||||
|  | ||||
| const response = await SmartRequest.create() | ||||
|   .url('https://api.example.com/data') | ||||
|   .option({ | ||||
|     agent: new Agent({ keepAlive: true }), // Custom agent | ||||
|     socketPath: '/var/run/api.sock',       // Unix socket | ||||
|   }) | ||||
|   .get(); | ||||
| ``` | ||||
|  | ||||
| ## Complete Example: Building a REST API Client | ||||
|  | ||||
| Here's a complete example of building a typed API client: | ||||
|  | ||||
| ```typescript | ||||
| import { SmartRequestClient, type SmartResponse } from '@push.rocks/smartrequest'; | ||||
| import { SmartRequest, type CoreResponse } from '@push.rocks/smartrequest'; | ||||
|  | ||||
| interface User { | ||||
|   id: number; | ||||
| @@ -308,7 +398,7 @@ class BlogApiClient { | ||||
|   private baseUrl = 'https://jsonplaceholder.typicode.com'; | ||||
|    | ||||
|   private async request(path: string) { | ||||
|     return SmartRequestClient.create() | ||||
|     return SmartRequest.create() | ||||
|       .url(`${this.baseUrl}${path}`) | ||||
|       .header('Accept', 'application/json'); | ||||
|   } | ||||
| @@ -354,11 +444,11 @@ const posts = await api.getAllPosts(user.id); | ||||
| ## Error Handling | ||||
|  | ||||
| ```typescript | ||||
| import { SmartRequestClient } from '@push.rocks/smartrequest'; | ||||
| import { SmartRequest } from '@push.rocks/smartrequest'; | ||||
|  | ||||
| async function fetchWithErrorHandling(url: string) { | ||||
|   try { | ||||
|     const response = await SmartRequestClient.create() | ||||
|     const response = await SmartRequest.create() | ||||
|       .url(url) | ||||
|       .timeout(5000) | ||||
|       .retry(2) | ||||
| @@ -384,6 +474,8 @@ async function fetchWithErrorHandling(url: string) { | ||||
|       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); | ||||
|     } | ||||
| @@ -392,30 +484,14 @@ async function fetchWithErrorHandling(url: string) { | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ## Legacy API | ||||
| ## Migrating from v2.x to v3.x | ||||
|  | ||||
| For backward compatibility, the original function-based API is still available via a separate import: | ||||
| Version 3.0 brings significant architectural improvements and a more consistent API: | ||||
|  | ||||
| ```typescript | ||||
| import { getJson, postJson, request } from '@push.rocks/smartrequest/legacy'; | ||||
|  | ||||
| // Simple GET request | ||||
| const response = await getJson('https://api.example.com/data'); | ||||
| console.log(response.body); | ||||
|  | ||||
| // POST request | ||||
| const result = await postJson('https://api.example.com/users', { | ||||
|   requestBody: { name: 'John', email: 'john@example.com' } | ||||
| }); | ||||
| ``` | ||||
|  | ||||
| For migration from the legacy API to the modern API, here's a quick reference: | ||||
|  | ||||
| | Legacy API | Modern API | | ||||
| |------------|------------| | ||||
| | `getJson(url)` | `SmartRequestClient.create().url(url).get()` | | ||||
| | `postJson(url, { requestBody: data })` | `SmartRequestClient.create().url(url).json(data).post()` | | ||||
| | `request(url, options)` | `SmartRequestClient.create().url(url).[...configure].get()` | | ||||
| 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. | ||||
| 3. **Stream Changes**: The `stream()` method now returns a web-style ReadableStream on all platforms. Use `streamNode()` for Node.js streams. | ||||
| 4. **Cross-Platform by Default**: The library now works in browsers out of the box with automatic platform detection. | ||||
|  | ||||
| ## License and Legal Information | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { tap, expect } from '@pushrocks/tapbundle'; | ||||
| import { tap, expect } from '@git.zone/tstest/tapbundle'; | ||||
|  | ||||
| // For browser tests, we need to import from a browser-safe path | ||||
| // that doesn't trigger Node.js module imports | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import { tap, expect } from '@pushrocks/tapbundle'; | ||||
| import { tap, expect } from '@git.zone/tstest/tapbundle'; | ||||
|  | ||||
| import { SmartRequestClient } from '../ts/client/index.js'; | ||||
| import { SmartRequest } from '../ts/client/index.js'; | ||||
|  | ||||
| tap.test('client: should request a html document over https', async () => { | ||||
|   const response = await SmartRequestClient.create() | ||||
|   const response = await SmartRequest.create() | ||||
|     .url('https://encrypted.google.com/') | ||||
|     .get(); | ||||
|  | ||||
| @@ -15,7 +15,7 @@ tap.test('client: should request a html document over https', async () => { | ||||
| }); | ||||
|  | ||||
| tap.test('client: should request a JSON document over https', async () => { | ||||
|   const response = await SmartRequestClient.create() | ||||
|   const response = await SmartRequest.create() | ||||
|     .url('https://jsonplaceholder.typicode.com/posts/1') | ||||
|     .get(); | ||||
|  | ||||
| @@ -26,7 +26,7 @@ tap.test('client: should request a JSON document over https', async () => { | ||||
|  | ||||
| tap.test('client: should post a JSON document over http', async () => { | ||||
|   const testData = { text: 'example_text' }; | ||||
|   const response = await SmartRequestClient.create() | ||||
|   const response = await SmartRequest.create() | ||||
|     .url('https://httpbin.org/post') | ||||
|     .json(testData) | ||||
|     .post(); | ||||
| @@ -41,7 +41,7 @@ tap.test('client: should set headers correctly', async () => { | ||||
|   const customHeader = 'X-Custom-Header'; | ||||
|   const headerValue = 'test-value'; | ||||
|  | ||||
|   const response = await SmartRequestClient.create() | ||||
|   const response = await SmartRequest.create() | ||||
|     .url('https://httpbin.org/headers') | ||||
|     .header(customHeader, headerValue) | ||||
|     .get(); | ||||
| @@ -57,7 +57,7 @@ tap.test('client: should set headers correctly', async () => { | ||||
| tap.test('client: should handle query parameters', async () => { | ||||
|   const params = { param1: 'value1', param2: 'value2' }; | ||||
|  | ||||
|   const response = await SmartRequestClient.create() | ||||
|   const response = await SmartRequest.create() | ||||
|     .url('https://httpbin.org/get') | ||||
|     .query(params) | ||||
|     .get(); | ||||
| @@ -72,7 +72,7 @@ tap.test('client: should handle query parameters', async () => { | ||||
|  | ||||
| tap.test('client: should handle timeout configuration', async () => { | ||||
|   // This test just verifies that the timeout method doesn't throw | ||||
|   const client = SmartRequestClient.create() | ||||
|   const client = SmartRequest.create() | ||||
|     .url('https://httpbin.org/get') | ||||
|     .timeout(5000); | ||||
|  | ||||
| @@ -83,7 +83,7 @@ tap.test('client: should handle timeout configuration', async () => { | ||||
|  | ||||
| tap.test('client: should handle retry configuration', async () => { | ||||
|   // This test just verifies that the retry method doesn't throw | ||||
|   const client = SmartRequestClient.create() | ||||
|   const client = SmartRequest.create() | ||||
|     .url('https://httpbin.org/get') | ||||
|     .retry(1); | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| // Export the main client | ||||
| export { SmartRequestClient } from './smartrequestclient.js'; | ||||
| export { SmartRequest } from './smartrequest.js'; | ||||
|  | ||||
| // Export response type from core | ||||
| export { CoreResponse } from '../core/index.js'; | ||||
| @@ -17,32 +17,32 @@ export { | ||||
| } from './types/pagination.js'; | ||||
|  | ||||
| // Convenience factory functions | ||||
| import { SmartRequestClient } from './smartrequestclient.js'; | ||||
| import { SmartRequest } from './smartrequest.js'; | ||||
|  | ||||
| /** | ||||
|  * Create a client pre-configured for JSON requests | ||||
|  */ | ||||
| export function createJsonClient<T = any>() { | ||||
|   return SmartRequestClient.create<T>(); | ||||
|   return SmartRequest.create<T>(); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Create a client pre-configured for form data requests | ||||
|  */ | ||||
| export function createFormClient<T = any>() { | ||||
|   return SmartRequestClient.create<T>(); | ||||
|   return SmartRequest.create<T>(); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Create a client pre-configured for binary data | ||||
|  */ | ||||
| export function createBinaryClient<T = any>() { | ||||
|   return SmartRequestClient.create<T>().accept('binary'); | ||||
|   return SmartRequest.create<T>().accept('binary'); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Create a client pre-configured for streaming | ||||
|  */ | ||||
| export function createStreamClient() { | ||||
|   return SmartRequestClient.create().accept('stream'); | ||||
|   return SmartRequest.create().accept('stream'); | ||||
| } | ||||
| @@ -17,7 +17,7 @@ import { createPaginatedResponse } from './features/pagination.js'; | ||||
| /** | ||||
|  * Modern fluent client for making HTTP requests | ||||
|  */ | ||||
| export class SmartRequestClient<T = any> { | ||||
| export class SmartRequest<T = any> { | ||||
|   private _url: string; | ||||
|   private _options: ICoreRequestOptions = {}; | ||||
|   private _retries: number = 0; | ||||
| @@ -25,10 +25,10 @@ export class SmartRequestClient<T = any> { | ||||
|   private _paginationConfig?: TPaginationConfig; | ||||
| 
 | ||||
|   /** | ||||
|    * Create a new SmartRequestClient instance | ||||
|    * Create a new SmartRequest instance | ||||
|    */ | ||||
|   static create<T = any>(): SmartRequestClient<T> { | ||||
|     return new SmartRequestClient<T>(); | ||||
|   static create<T = any>(): SmartRequest<T> { | ||||
|     return new SmartRequest<T>(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
| @@ -278,7 +278,7 @@ export class SmartRequestClient<T = any> { | ||||
|       this._queryParams, | ||||
|       (nextPageParams) => { | ||||
|         // Create a new client with the same configuration but updated query params
 | ||||
|         const nextClient = new SmartRequestClient<ItemType>(); | ||||
|         const nextClient = new SmartRequest<ItemType>(); | ||||
|         Object.assign(nextClient, this); | ||||
|         nextClient._queryParams = nextPageParams; | ||||
| 
 | ||||
| @@ -6,5 +6,5 @@ export { CoreResponse } from './core/index.js'; | ||||
| export type { ICoreRequestOptions, ICoreResponse } from './core_base/types.js'; | ||||
|  | ||||
| // Default export for easier importing | ||||
| import { SmartRequestClient } from './client/smartrequestclient.js'; | ||||
| export default SmartRequestClient; | ||||
| import { SmartRequest } from './client/smartrequest.js'; | ||||
| export default SmartRequest; | ||||
		Reference in New Issue
	
	Block a user