diff --git a/ts/core_base/index.ts b/ts/core_base/index.ts new file mode 100644 index 0000000..1fba886 --- /dev/null +++ b/ts/core_base/index.ts @@ -0,0 +1,4 @@ +// Core base exports - abstract classes and platform-agnostic types +export * from './types.js'; +export * from './request.js'; +export * from './response.js'; \ No newline at end of file diff --git a/ts/core_base/request.ts b/ts/core_base/request.ts new file mode 100644 index 0000000..635a4fb --- /dev/null +++ b/ts/core_base/request.ts @@ -0,0 +1,45 @@ +import * as types from './types.js'; + +/** + * Abstract Core Request class that defines the interface for all HTTP/HTTPS requests + */ +export abstract class CoreRequest { + /** + * Tests if a URL is a unix socket + */ + static isUnixSocket(url: string): boolean { + const unixRegex = /^(http:\/\/|https:\/\/|)unix:/; + return unixRegex.test(url); + } + + /** + * Parses socket path and route from unix socket URL + */ + static parseUnixSocketUrl(url: string): { socketPath: string; path: string } { + const parseRegex = /(.*):(.*)/; + const result = parseRegex.exec(url); + return { + socketPath: result[1], + path: result[2], + }; + } + + protected url: string; + protected options: TOptions; + + constructor(url: string, options: TOptions) { + this.url = url; + this.options = options; + } + + /** + * Fire the request and return a response + */ + abstract fire(): Promise; + + /** + * Fire the request and return the raw response (platform-specific) + */ + abstract fireCore(): Promise; + +} \ No newline at end of file diff --git a/ts/core_base/response.ts b/ts/core_base/response.ts new file mode 100644 index 0000000..d946b56 --- /dev/null +++ b/ts/core_base/response.ts @@ -0,0 +1,40 @@ +import * as types from './types.js'; + +/** + * Abstract Core Response class that provides a fetch-like API + */ +export abstract class CoreResponse implements types.IAbstractResponse { + protected consumed = false; + + // Public properties + public abstract readonly ok: boolean; + public abstract readonly status: number; + public abstract readonly statusText: string; + public abstract readonly headers: types.AbstractHeaders; + public abstract readonly url: string; + + /** + * Ensures the body can only be consumed once + */ + protected ensureNotConsumed(): void { + if (this.consumed) { + throw new Error('Body has already been consumed'); + } + this.consumed = true; + } + + /** + * Parse response as JSON + */ + abstract json(): Promise; + + /** + * Get response as text + */ + abstract text(): Promise; + + /** + * Get response as ArrayBuffer + */ + abstract arrayBuffer(): Promise; +} \ No newline at end of file diff --git a/ts/core_base/types.ts b/ts/core_base/types.ts new file mode 100644 index 0000000..a23e02a --- /dev/null +++ b/ts/core_base/types.ts @@ -0,0 +1,62 @@ +/** + * HTTP Methods supported + */ +export type THttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS'; + +/** + * Response types supported + */ +export type ResponseType = 'json' | 'text' | 'binary' | 'stream'; + +/** + * Form field data for multipart/form-data requests + */ +export interface IFormField { + name: string; + value: string | Buffer; + filename?: string; + contentType?: string; +} + +/** + * URL encoded form field + */ +export interface IUrlEncodedField { + key: string; + value: string; +} + +/** + * Abstract request options - platform agnostic + */ +export interface IAbstractRequestOptions { + method?: THttpMethod | string; // Allow string for compatibility + headers?: any; // Allow any for platform compatibility + keepAlive?: boolean; + requestBody?: any; + queryParams?: { [key: string]: string }; + timeout?: number; + hardDataCuttingTimeout?: number; +} + +/** + * Abstract response headers - platform agnostic + */ +export type AbstractHeaders = Record; + +/** + * Abstract response interface - platform agnostic + */ +export interface IAbstractResponse { + // Properties + ok: boolean; + status: number; + statusText: string; + headers: AbstractHeaders; + url: string; + + // Methods + json(): Promise; + text(): Promise; + arrayBuffer(): Promise; +} \ No newline at end of file diff --git a/ts/core_node/request.ts b/ts/core_node/request.ts index 158e40a..832301b 100644 --- a/ts/core_node/request.ts +++ b/ts/core_node/request.ts @@ -1,6 +1,7 @@ import * as plugins from './plugins.js'; import * as types from './types.js'; import { CoreResponse } from './response.js'; +import { CoreRequest as AbstractCoreRequest } from '../core_base/request.js'; // Keep-alive agents for connection pooling const httpAgent = new plugins.agentkeepalive.HttpAgent({ @@ -26,30 +27,9 @@ const httpsAgentKeepAliveFalse = new plugins.agentkeepalive.HttpsAgent({ }); /** - * Core Request class that handles all HTTP/HTTPS requests + * Node.js implementation of Core Request class that handles all HTTP/HTTPS requests */ -export class CoreRequest { - /** - * Tests if a URL is a unix socket - */ - static isUnixSocket(url: string): boolean { - const unixRegex = /^(http:\/\/|https:\/\/|)unix:/; - return unixRegex.test(url); - } - - /** - * Parses socket path and route from unix socket URL - */ - static parseUnixSocketUrl(url: string): { socketPath: string; path: string } { - const parseRegex = /(.*):(.*)/; - const result = parseRegex.exec(url); - return { - socketPath: result[1], - path: result[2], - }; - } - private url: string; - private options: types.ICoreRequestOptions; +export class CoreRequest extends AbstractCoreRequest { private requestDataFunc: ((req: plugins.http.ClientRequest) => void) | null; constructor( @@ -57,8 +37,7 @@ export class CoreRequest { options: types.ICoreRequestOptions = {}, requestDataFunc: ((req: plugins.http.ClientRequest) => void) | null = null ) { - this.url = url; - this.options = options; + super(url, options); this.requestDataFunc = requestDataFunc; } diff --git a/ts/core_node/response.ts b/ts/core_node/response.ts index cae4ce4..d390cf1 100644 --- a/ts/core_node/response.ts +++ b/ts/core_node/response.ts @@ -1,13 +1,13 @@ import * as plugins from './plugins.js'; import * as types from './types.js'; +import { CoreResponse as AbstractCoreResponse } from '../core_base/response.js'; /** - * Core Response class that provides a fetch-like API + * Node.js implementation of Core Response class that provides a fetch-like API */ -export class CoreResponse implements types.ICoreResponse { +export class CoreResponse extends AbstractCoreResponse implements types.ICoreResponse { private incomingMessage: plugins.http.IncomingMessage; private bodyBufferPromise: Promise | null = null; - private consumed = false; // Public properties public readonly ok: boolean; @@ -17,6 +17,7 @@ export class CoreResponse implements types.ICoreResponse { public readonly url: string; constructor(incomingMessage: plugins.http.IncomingMessage, url: string) { + super(); this.incomingMessage = incomingMessage; this.url = url; this.status = incomingMessage.statusCode || 0; @@ -25,16 +26,6 @@ export class CoreResponse implements types.ICoreResponse { this.headers = incomingMessage.headers; } - /** - * Ensures the body can only be consumed once - */ - private ensureNotConsumed(): void { - if (this.consumed) { - throw new Error('Body has already been consumed'); - } - this.consumed = true; - } - /** * Collects the body as a buffer */ diff --git a/ts/core_node/types.ts b/ts/core_node/types.ts index 539ef7d..c087eb3 100644 --- a/ts/core_node/types.ts +++ b/ts/core_node/types.ts @@ -1,25 +1,19 @@ import * as plugins from './plugins.js'; +import * as baseTypes from '../core_base/types.js'; + +// Re-export base types +export * from '../core_base/types.js'; /** * Core request options extending Node.js RequestOptions */ -export interface ICoreRequestOptions extends plugins.https.RequestOptions { +export interface ICoreRequestOptions extends plugins.https.RequestOptions, Omit { keepAlive?: boolean; requestBody?: any; queryParams?: { [key: string]: string }; hardDataCuttingTimeout?: number; } -/** - * HTTP Methods supported - */ -export type THttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS'; - -/** - * Response types supported - */ -export type ResponseType = 'json' | 'text' | 'binary' | 'stream'; - /** * Extended IncomingMessage with body property (legacy compatibility) */ @@ -28,27 +22,9 @@ export interface IExtendedIncomingMessage extends plugins.http.Incoming } /** - * Form field data for multipart/form-data requests + * Core response object that provides fetch-like API with Node.js specific methods */ -export interface IFormField { - name: string; - value: string | Buffer; - filename?: string; - contentType?: string; -} - -/** - * URL encoded form field - */ -export interface IUrlEncodedField { - key: string; - value: string; -} - -/** - * Core response object that provides fetch-like API - */ -export interface ICoreResponse { +export interface ICoreResponse extends baseTypes.IAbstractResponse { // Properties ok: boolean; status: number; diff --git a/ts/index.ts b/ts/index.ts index 9ae8b0a..052ee09 100644 --- a/ts/index.ts +++ b/ts/index.ts @@ -5,7 +5,7 @@ export * from './legacy/index.js'; export * from './modern/index.js'; // Core exports for advanced usage -export { CoreResponse, type ICoreRequestOptions, type ICoreResponse } from './core/index.js'; +export { CoreResponse, type ICoreRequestOptions, type ICoreResponse } from './core_node/index.js'; // Default export for easier importing import { SmartRequestClient } from './modern/smartrequestclient.js'; diff --git a/ts/legacy/adapter.ts b/ts/legacy/adapter.ts index 699bdbb..35abd55 100644 --- a/ts/legacy/adapter.ts +++ b/ts/legacy/adapter.ts @@ -3,13 +3,13 @@ * Maps legacy API to the new core module */ -import * as core from '../core/index.js'; -import * as plugins from '../core/plugins.js'; +import * as core from '../core_node/index.js'; +import * as plugins from '../core_node/plugins.js'; const smartpromise = plugins.smartpromise; // Re-export types for backward compatibility -export { type IExtendedIncomingMessage } from '../core/types.js'; +export { type IExtendedIncomingMessage } from '../core_node/types.js'; export interface ISmartRequestOptions extends core.ICoreRequestOptions { autoJsonParse?: boolean; responseType?: 'json' | 'text' | 'binary' | 'stream'; diff --git a/ts/modern/features/pagination.ts b/ts/modern/features/pagination.ts index 4bb85cf..16ce099 100644 --- a/ts/modern/features/pagination.ts +++ b/ts/modern/features/pagination.ts @@ -1,4 +1,4 @@ -import { type CoreResponse } from '../../core/index.js'; +import { type CoreResponse } from '../../core_node/index.js'; import { type TPaginationConfig, PaginationStrategy, type TPaginatedResponse } from '../types/pagination.js'; /** diff --git a/ts/modern/index.ts b/ts/modern/index.ts index b823d3a..988e790 100644 --- a/ts/modern/index.ts +++ b/ts/modern/index.ts @@ -2,7 +2,7 @@ export { SmartRequestClient } from './smartrequestclient.js'; // Export response type from core -export { CoreResponse } from '../core/index.js'; +export { CoreResponse } from '../core_node/index.js'; // Export types export type { HttpMethod, ResponseType, FormField, RetryConfig, TimeoutConfig } from './types/common.js'; diff --git a/ts/modern/smartrequestclient.ts b/ts/modern/smartrequestclient.ts index 8289784..9616603 100644 --- a/ts/modern/smartrequestclient.ts +++ b/ts/modern/smartrequestclient.ts @@ -1,5 +1,5 @@ -import { CoreRequest, CoreResponse, type ICoreRequestOptions } from '../core/index.js'; -import * as plugins from '../core/plugins.js'; +import { CoreRequest, CoreResponse, type ICoreRequestOptions } from '../core_node/index.js'; +import * as plugins from '../core_node/plugins.js'; import type { HttpMethod, ResponseType, FormField } from './types/common.js'; import { diff --git a/ts/modern/types/pagination.ts b/ts/modern/types/pagination.ts index 0cf7993..5c38d73 100644 --- a/ts/modern/types/pagination.ts +++ b/ts/modern/types/pagination.ts @@ -1,4 +1,4 @@ -import { type CoreResponse } from '../../core/index.js'; +import { type CoreResponse } from '../../core_node/index.js'; /** * Pagination strategy options