update
This commit is contained in:
@@ -40,6 +40,7 @@
|
|||||||
"homepage": "https://code.foss.global/push.rocks/smartrequest",
|
"homepage": "https://code.foss.global/push.rocks/smartrequest",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@push.rocks/smartenv": "^5.0.13",
|
"@push.rocks/smartenv": "^5.0.13",
|
||||||
|
"@push.rocks/smartpath": "^6.0.0",
|
||||||
"@push.rocks/smartpromise": "^4.0.4",
|
"@push.rocks/smartpromise": "^4.0.4",
|
||||||
"@push.rocks/smarturl": "^3.1.0",
|
"@push.rocks/smarturl": "^3.1.0",
|
||||||
"agentkeepalive": "^4.5.0",
|
"agentkeepalive": "^4.5.0",
|
||||||
|
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@@ -11,6 +11,9 @@ importers:
|
|||||||
'@push.rocks/smartenv':
|
'@push.rocks/smartenv':
|
||||||
specifier: ^5.0.13
|
specifier: ^5.0.13
|
||||||
version: 5.0.13
|
version: 5.0.13
|
||||||
|
'@push.rocks/smartpath':
|
||||||
|
specifier: ^6.0.0
|
||||||
|
version: 6.0.0
|
||||||
'@push.rocks/smartpromise':
|
'@push.rocks/smartpromise':
|
||||||
specifier: ^4.0.4
|
specifier: ^4.0.4
|
||||||
version: 4.2.3
|
version: 4.2.3
|
||||||
@@ -830,6 +833,9 @@ packages:
|
|||||||
'@push.rocks/smartpath@5.0.18':
|
'@push.rocks/smartpath@5.0.18':
|
||||||
resolution: {integrity: sha512-kIyRTlOoeEth5b4Qp8KPUxNOGNdvhb2aD0hbHfF3oGTQ0xnDdgB1l03/4bIoapHG48OrTgh4uQ5tUorykgdOzw==}
|
resolution: {integrity: sha512-kIyRTlOoeEth5b4Qp8KPUxNOGNdvhb2aD0hbHfF3oGTQ0xnDdgB1l03/4bIoapHG48OrTgh4uQ5tUorykgdOzw==}
|
||||||
|
|
||||||
|
'@push.rocks/smartpath@6.0.0':
|
||||||
|
resolution: {integrity: sha512-r94u1MbBaIOSy+517PZp2P7SuZPSe9LkwJ8l3dXQKHeIOri/zDxk/RQPiFM+j4N9301ztkRyhvRj7xgUDroOsg==}
|
||||||
|
|
||||||
'@push.rocks/smartpdf@3.2.2':
|
'@push.rocks/smartpdf@3.2.2':
|
||||||
resolution: {integrity: sha512-SKGNHz7HsgU6uVSVrRCL13kIeAFMvd4oQBLI3VmPcMkxXfWNPJkb6jKknqP8bhobWA/ryJS+3Dj///UELUvVKQ==}
|
resolution: {integrity: sha512-SKGNHz7HsgU6uVSVrRCL13kIeAFMvd4oQBLI3VmPcMkxXfWNPJkb6jKknqP8bhobWA/ryJS+3Dj///UELUvVKQ==}
|
||||||
|
|
||||||
@@ -5725,6 +5731,8 @@ snapshots:
|
|||||||
|
|
||||||
'@push.rocks/smartpath@5.0.18': {}
|
'@push.rocks/smartpath@5.0.18': {}
|
||||||
|
|
||||||
|
'@push.rocks/smartpath@6.0.0': {}
|
||||||
|
|
||||||
'@push.rocks/smartpdf@3.2.2(typescript@5.7.3)':
|
'@push.rocks/smartpdf@3.2.2(typescript@5.7.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@push.rocks/smartbuffer': 3.0.4
|
'@push.rocks/smartbuffer': 3.0.4
|
||||||
|
102
test/test.browser.ts
Normal file
102
test/test.browser.ts
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import { tap, expect } from '@pushrocks/tapbundle';
|
||||||
|
|
||||||
|
// For browser tests, we need to import from a browser-safe path
|
||||||
|
// that doesn't trigger Node.js module imports
|
||||||
|
import { CoreRequest, CoreResponse } from '../ts/core/index.js';
|
||||||
|
import type { ICoreRequestOptions } from '../ts/core_base/types.js';
|
||||||
|
|
||||||
|
tap.test('browser: should request a JSON document over https', async () => {
|
||||||
|
const request = new CoreRequest('https://jsonplaceholder.typicode.com/posts/1');
|
||||||
|
const response = await request.fire();
|
||||||
|
|
||||||
|
expect(response).not.toBeNull();
|
||||||
|
expect(response).toHaveProperty('status');
|
||||||
|
expect(response.status).toEqual(200);
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
expect(data).toHaveProperty('id');
|
||||||
|
expect(data.id).toEqual(1);
|
||||||
|
expect(data).toHaveProperty('title');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('browser: should handle CORS requests', async () => {
|
||||||
|
const options: ICoreRequestOptions = {
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/vnd.github.v3+json'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const request = new CoreRequest('https://api.github.com/users/github', options);
|
||||||
|
const response = await request.fire();
|
||||||
|
|
||||||
|
expect(response).not.toBeNull();
|
||||||
|
expect(response.status).toEqual(200);
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
expect(data).toHaveProperty('login');
|
||||||
|
expect(data.login).toEqual('github');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('browser: should handle request timeouts', async () => {
|
||||||
|
let timedOut = false;
|
||||||
|
|
||||||
|
const options: ICoreRequestOptions = {
|
||||||
|
timeout: 1000
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const request = new CoreRequest('https://httpbin.org/delay/10', options);
|
||||||
|
await request.fire();
|
||||||
|
} catch (error) {
|
||||||
|
timedOut = true;
|
||||||
|
expect(error.message).toContain('timed out');
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(timedOut).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('browser: should handle POST requests with JSON', async () => {
|
||||||
|
const testData = {
|
||||||
|
title: 'foo',
|
||||||
|
body: 'bar',
|
||||||
|
userId: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const options: ICoreRequestOptions = {
|
||||||
|
method: 'POST',
|
||||||
|
requestBody: testData
|
||||||
|
};
|
||||||
|
|
||||||
|
const request = new CoreRequest('https://jsonplaceholder.typicode.com/posts', options);
|
||||||
|
const response = await request.fire();
|
||||||
|
|
||||||
|
expect(response.status).toEqual(201);
|
||||||
|
|
||||||
|
const responseData = await response.json();
|
||||||
|
expect(responseData).toHaveProperty('id');
|
||||||
|
expect(responseData.title).toEqual(testData.title);
|
||||||
|
expect(responseData.body).toEqual(testData.body);
|
||||||
|
expect(responseData.userId).toEqual(testData.userId);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('browser: should handle query parameters', async () => {
|
||||||
|
const options: ICoreRequestOptions = {
|
||||||
|
queryParams: {
|
||||||
|
foo: 'bar',
|
||||||
|
baz: 'qux'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const request = new CoreRequest('https://httpbin.org/get', options);
|
||||||
|
const response = await request.fire();
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200);
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
expect(data.args).toHaveProperty('foo');
|
||||||
|
expect(data.args.foo).toEqual('bar');
|
||||||
|
expect(data.args).toHaveProperty('baz');
|
||||||
|
expect(data.args.baz).toEqual('qux');
|
||||||
|
});
|
||||||
|
|
||||||
|
export default tap.start();
|
@@ -1,8 +1,8 @@
|
|||||||
import { tap, expect } from '@pushrocks/tapbundle';
|
import { tap, expect } from '@pushrocks/tapbundle';
|
||||||
|
|
||||||
import { SmartRequestClient } from '../ts/modern/index.js';
|
import { SmartRequestClient } from '../ts/client/index.js';
|
||||||
|
|
||||||
tap.test('modern: should request a html document over https', async () => {
|
tap.test('client: should request a html document over https', async () => {
|
||||||
const response = await SmartRequestClient.create()
|
const response = await SmartRequestClient.create()
|
||||||
.url('https://encrypted.google.com/')
|
.url('https://encrypted.google.com/')
|
||||||
.get();
|
.get();
|
||||||
@@ -14,7 +14,7 @@ tap.test('modern: should request a html document over https', async () => {
|
|||||||
expect(text.length).toBeGreaterThan(0);
|
expect(text.length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('modern: should request a JSON document over https', async () => {
|
tap.test('client: should request a JSON document over https', async () => {
|
||||||
const response = await SmartRequestClient.create()
|
const response = await SmartRequestClient.create()
|
||||||
.url('https://jsonplaceholder.typicode.com/posts/1')
|
.url('https://jsonplaceholder.typicode.com/posts/1')
|
||||||
.get();
|
.get();
|
||||||
@@ -24,7 +24,7 @@ tap.test('modern: should request a JSON document over https', async () => {
|
|||||||
expect(body.id).toEqual(1);
|
expect(body.id).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('modern: should post a JSON document over http', async () => {
|
tap.test('client: should post a JSON document over http', async () => {
|
||||||
const testData = { text: 'example_text' };
|
const testData = { text: 'example_text' };
|
||||||
const response = await SmartRequestClient.create()
|
const response = await SmartRequestClient.create()
|
||||||
.url('https://httpbin.org/post')
|
.url('https://httpbin.org/post')
|
||||||
@@ -37,7 +37,7 @@ tap.test('modern: should post a JSON document over http', async () => {
|
|||||||
expect(body.json.text).toEqual('example_text');
|
expect(body.json.text).toEqual('example_text');
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('modern: should set headers correctly', async () => {
|
tap.test('client: should set headers correctly', async () => {
|
||||||
const customHeader = 'X-Custom-Header';
|
const customHeader = 'X-Custom-Header';
|
||||||
const headerValue = 'test-value';
|
const headerValue = 'test-value';
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ tap.test('modern: should set headers correctly', async () => {
|
|||||||
expect(body.headers[customHeader]).toEqual(headerValue);
|
expect(body.headers[customHeader]).toEqual(headerValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('modern: should handle query parameters', async () => {
|
tap.test('client: should handle query parameters', async () => {
|
||||||
const params = { param1: 'value1', param2: 'value2' };
|
const params = { param1: 'value1', param2: 'value2' };
|
||||||
|
|
||||||
const response = await SmartRequestClient.create()
|
const response = await SmartRequestClient.create()
|
||||||
@@ -70,7 +70,7 @@ tap.test('modern: should handle query parameters', async () => {
|
|||||||
expect(body.args.param2).toEqual('value2');
|
expect(body.args.param2).toEqual('value2');
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('modern: should handle timeout configuration', async () => {
|
tap.test('client: should handle timeout configuration', async () => {
|
||||||
// This test just verifies that the timeout method doesn't throw
|
// This test just verifies that the timeout method doesn't throw
|
||||||
const client = SmartRequestClient.create()
|
const client = SmartRequestClient.create()
|
||||||
.url('https://httpbin.org/get')
|
.url('https://httpbin.org/get')
|
||||||
@@ -81,7 +81,7 @@ tap.test('modern: should handle timeout configuration', async () => {
|
|||||||
expect(response.ok).toBeTrue();
|
expect(response.ok).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('modern: should handle retry configuration', async () => {
|
tap.test('client: should handle retry configuration', async () => {
|
||||||
// This test just verifies that the retry method doesn't throw
|
// This test just verifies that the retry method doesn't throw
|
||||||
const client = SmartRequestClient.create()
|
const client = SmartRequestClient.create()
|
||||||
.url('https://httpbin.org/get')
|
.url('https://httpbin.org/get')
|
@@ -1,17 +1,18 @@
|
|||||||
import { type CoreResponse } from '../../core_node/index.js';
|
import { type CoreResponse } from '../../core/index.js';
|
||||||
|
import type { ICoreResponse } from '../../core_base/types.js';
|
||||||
import { type TPaginationConfig, PaginationStrategy, type TPaginatedResponse } from '../types/pagination.js';
|
import { type TPaginationConfig, PaginationStrategy, type TPaginatedResponse } from '../types/pagination.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a paginated response from a regular response
|
* Creates a paginated response from a regular response
|
||||||
*/
|
*/
|
||||||
export async function createPaginatedResponse<T>(
|
export async function createPaginatedResponse<T>(
|
||||||
response: CoreResponse<any>,
|
response: ICoreResponse<any>,
|
||||||
paginationConfig: TPaginationConfig,
|
paginationConfig: TPaginationConfig,
|
||||||
queryParams: Record<string, string>,
|
queryParams: Record<string, string>,
|
||||||
fetchNextPage: (params: Record<string, string>) => Promise<TPaginatedResponse<T>>
|
fetchNextPage: (params: Record<string, string>) => Promise<TPaginatedResponse<T>>
|
||||||
): Promise<TPaginatedResponse<T>> {
|
): Promise<TPaginatedResponse<T>> {
|
||||||
// Parse response body first
|
// Parse response body first
|
||||||
const body = await response.json();
|
const body = await response.json() as any;
|
||||||
|
|
||||||
// Default to response.body for items if response is JSON
|
// Default to response.body for items if response is JSON
|
||||||
let items: T[] = Array.isArray(body)
|
let items: T[] = Array.isArray(body)
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
export { SmartRequestClient } from './smartrequestclient.js';
|
export { SmartRequestClient } from './smartrequestclient.js';
|
||||||
|
|
||||||
// Export response type from core
|
// Export response type from core
|
||||||
export { CoreResponse } from '../core_node/index.js';
|
export { CoreResponse } from '../core/index.js';
|
||||||
|
|
||||||
// Export types
|
// Export types
|
||||||
export type { HttpMethod, ResponseType, FormField, RetryConfig, TimeoutConfig } from './types/common.js';
|
export type { HttpMethod, ResponseType, FormField, RetryConfig, TimeoutConfig } from './types/common.js';
|
||||||
|
6
ts/client/plugins.ts
Normal file
6
ts/client/plugins.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// plugins for client module
|
||||||
|
import FormData from 'form-data';
|
||||||
|
|
||||||
|
export {
|
||||||
|
FormData as formData
|
||||||
|
};
|
@@ -1,6 +1,7 @@
|
|||||||
import { CoreRequest, CoreResponse } from '../core/index.js';
|
import { CoreRequest, CoreResponse } from '../core/index.js';
|
||||||
import * as plugins from '../core_node/plugins.js';
|
import type { ICoreResponse } from '../core_base/types.js';
|
||||||
import type { IAbstractRequestOptions } from '../core_base/types.js';
|
import * as plugins from './plugins.js';
|
||||||
|
import type { ICoreRequestOptions } from '../core_base/types.js';
|
||||||
|
|
||||||
import type { HttpMethod, ResponseType, FormField } from './types/common.js';
|
import type { HttpMethod, ResponseType, FormField } from './types/common.js';
|
||||||
import {
|
import {
|
||||||
@@ -18,7 +19,7 @@ import { createPaginatedResponse } from './features/pagination.js';
|
|||||||
*/
|
*/
|
||||||
export class SmartRequestClient<T = any> {
|
export class SmartRequestClient<T = any> {
|
||||||
private _url: string;
|
private _url: string;
|
||||||
private _options: IAbstractRequestOptions = {};
|
private _options: ICoreRequestOptions = {};
|
||||||
private _retries: number = 0;
|
private _retries: number = 0;
|
||||||
private _queryParams: Record<string, string> = {};
|
private _queryParams: Record<string, string> = {};
|
||||||
private _paginationConfig?: TPaginationConfig;
|
private _paginationConfig?: TPaginationConfig;
|
||||||
@@ -224,35 +225,35 @@ export class SmartRequestClient<T = any> {
|
|||||||
/**
|
/**
|
||||||
* Make a GET request
|
* Make a GET request
|
||||||
*/
|
*/
|
||||||
async get<R = T>(): Promise<CoreResponse<R>> {
|
async get<R = T>(): Promise<ICoreResponse<R>> {
|
||||||
return this.execute<R>('GET');
|
return this.execute<R>('GET');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a POST request
|
* Make a POST request
|
||||||
*/
|
*/
|
||||||
async post<R = T>(): Promise<CoreResponse<R>> {
|
async post<R = T>(): Promise<ICoreResponse<R>> {
|
||||||
return this.execute<R>('POST');
|
return this.execute<R>('POST');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a PUT request
|
* Make a PUT request
|
||||||
*/
|
*/
|
||||||
async put<R = T>(): Promise<CoreResponse<R>> {
|
async put<R = T>(): Promise<ICoreResponse<R>> {
|
||||||
return this.execute<R>('PUT');
|
return this.execute<R>('PUT');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a DELETE request
|
* Make a DELETE request
|
||||||
*/
|
*/
|
||||||
async delete<R = T>(): Promise<CoreResponse<R>> {
|
async delete<R = T>(): Promise<ICoreResponse<R>> {
|
||||||
return this.execute<R>('DELETE');
|
return this.execute<R>('DELETE');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a PATCH request
|
* Make a PATCH request
|
||||||
*/
|
*/
|
||||||
async patch<R = T>(): Promise<CoreResponse<R>> {
|
async patch<R = T>(): Promise<ICoreResponse<R>> {
|
||||||
return this.execute<R>('PATCH');
|
return this.execute<R>('PATCH');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,7 +298,7 @@ export class SmartRequestClient<T = any> {
|
|||||||
/**
|
/**
|
||||||
* Execute the HTTP request
|
* Execute the HTTP request
|
||||||
*/
|
*/
|
||||||
private async execute<R = T>(method?: HttpMethod): Promise<CoreResponse<R>> {
|
private async execute<R = T>(method?: HttpMethod): Promise<ICoreResponse<R>> {
|
||||||
if (method) {
|
if (method) {
|
||||||
this._options.method = method;
|
this._options.method = method;
|
||||||
}
|
}
|
||||||
@@ -309,8 +310,9 @@ export class SmartRequestClient<T = any> {
|
|||||||
|
|
||||||
for (let attempt = 0; attempt <= this._retries; attempt++) {
|
for (let attempt = 0; attempt <= this._retries; attempt++) {
|
||||||
try {
|
try {
|
||||||
const response = await CoreRequest.create(this._url, this._options);
|
const request = new CoreRequest(this._url, this._options as any);
|
||||||
return response as CoreResponse<R>;
|
const response = await request.fire();
|
||||||
|
return response as ICoreResponse<R>;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
lastError = error as Error;
|
lastError = error as Error;
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { type CoreResponse } from '../../core_node/index.js';
|
import { type CoreResponse } from '../../core/index.js';
|
||||||
|
import type { ICoreResponse } from '../../core_base/types.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pagination strategy options
|
* Pagination strategy options
|
||||||
@@ -45,8 +46,8 @@ export interface LinkPaginationConfig {
|
|||||||
*/
|
*/
|
||||||
export interface CustomPaginationConfig {
|
export interface CustomPaginationConfig {
|
||||||
strategy: PaginationStrategy.CUSTOM;
|
strategy: PaginationStrategy.CUSTOM;
|
||||||
hasNextPage: (response: CoreResponse<any>) => boolean;
|
hasNextPage: (response: ICoreResponse<any>) => boolean;
|
||||||
getNextPageParams: (response: CoreResponse<any>, currentParams: Record<string, string>) => Record<string, string>;
|
getNextPageParams: (response: ICoreResponse<any>, currentParams: Record<string, string>) => Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,5 +63,5 @@ export interface TPaginatedResponse<T> {
|
|||||||
hasNextPage: boolean; // Whether there are more pages
|
hasNextPage: boolean; // Whether there are more pages
|
||||||
getNextPage: () => Promise<TPaginatedResponse<T>>; // Function to get the next page
|
getNextPage: () => Promise<TPaginatedResponse<T>>; // Function to get the next page
|
||||||
getAllPages: () => Promise<T[]>; // Function to get all remaining pages and combine
|
getAllPages: () => Promise<T[]>; // Function to get all remaining pages and combine
|
||||||
response: CoreResponse<any>; // Original response
|
response: ICoreResponse<any>; // Original response
|
||||||
}
|
}
|
@@ -1,22 +1,30 @@
|
|||||||
import * as smartenv from '@push.rocks/smartenv';
|
import * as plugins from './plugins.js';
|
||||||
|
|
||||||
// Export all base types - these are the public API
|
// Export all base types - these are the public API
|
||||||
export * from '../core_base/types.js';
|
export * from '../core_base/types.js';
|
||||||
|
|
||||||
const smartenvInstance = new smartenv.Smartenv();
|
const smartenvInstance = new plugins.smartenv.Smartenv();
|
||||||
|
|
||||||
// Load the appropriate implementation based on environment
|
// Dynamically load the appropriate implementation
|
||||||
const implementation = await (async () => {
|
let CoreRequest: any;
|
||||||
if (smartenvInstance.isNode) {
|
let CoreResponse: any;
|
||||||
return smartenvInstance.getSafeNodeModule<typeof import('../core_node/index.js')>('../core_node/index.js');
|
|
||||||
} else {
|
|
||||||
return import('../core_fetch/index.js');
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
// Export the implementation classes
|
if (smartenvInstance.isNode) {
|
||||||
export const CoreRequest = implementation.CoreRequest;
|
// In Node.js, load the node implementation
|
||||||
export const CoreResponse = implementation.CoreResponse;
|
const modulePath = plugins.smartpath.join(
|
||||||
|
plugins.smartpath.dirname(import.meta.url),
|
||||||
|
'../core_node/index.js'
|
||||||
|
)
|
||||||
|
console.log(modulePath);
|
||||||
|
const impl = await smartenvInstance.getSafeNodeModule(modulePath);
|
||||||
|
CoreRequest = impl.CoreRequest;
|
||||||
|
CoreResponse = impl.CoreResponse;
|
||||||
|
} else {
|
||||||
|
// In browser, load the fetch implementation
|
||||||
|
const impl = await import('../core_fetch/index.js');
|
||||||
|
CoreRequest = impl.CoreRequest;
|
||||||
|
CoreResponse = impl.CoreResponse;
|
||||||
|
}
|
||||||
|
|
||||||
// Export CoreResponse as a type for type annotations
|
// Export the loaded implementations
|
||||||
export type CoreResponse<T = any> = InstanceType<typeof implementation.CoreResponse>;
|
export { CoreRequest, CoreResponse };
|
||||||
|
4
ts/core/plugins.ts
Normal file
4
ts/core/plugins.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import * as smartenv from '@push.rocks/smartenv';
|
||||||
|
import * as smartpath from '@push.rocks/smartpath/iso';
|
||||||
|
|
||||||
|
export { smartenv, smartpath };
|
@@ -3,7 +3,7 @@ import * as types from './types.js';
|
|||||||
/**
|
/**
|
||||||
* Abstract Core Request class that defines the interface for all HTTP/HTTPS requests
|
* Abstract Core Request class that defines the interface for all HTTP/HTTPS requests
|
||||||
*/
|
*/
|
||||||
export abstract class CoreRequest<TOptions extends types.IAbstractRequestOptions = types.IAbstractRequestOptions, TResponse = any> {
|
export abstract class CoreRequest<TOptions extends types.ICoreRequestOptions = types.ICoreRequestOptions, TResponse = any> {
|
||||||
/**
|
/**
|
||||||
* Tests if a URL is a unix socket
|
* Tests if a URL is a unix socket
|
||||||
*/
|
*/
|
||||||
|
@@ -3,14 +3,14 @@ import * as types from './types.js';
|
|||||||
/**
|
/**
|
||||||
* Abstract Core Response class that provides a fetch-like API
|
* Abstract Core Response class that provides a fetch-like API
|
||||||
*/
|
*/
|
||||||
export abstract class CoreResponse<T = any> implements types.IAbstractResponse<T> {
|
export abstract class CoreResponse<T = any> implements types.ICoreResponse<T> {
|
||||||
protected consumed = false;
|
protected consumed = false;
|
||||||
|
|
||||||
// Public properties
|
// Public properties
|
||||||
public abstract readonly ok: boolean;
|
public abstract readonly ok: boolean;
|
||||||
public abstract readonly status: number;
|
public abstract readonly status: number;
|
||||||
public abstract readonly statusText: string;
|
public abstract readonly statusText: string;
|
||||||
public abstract readonly headers: types.AbstractHeaders;
|
public abstract readonly headers: types.Headers;
|
||||||
public abstract readonly url: string;
|
public abstract readonly url: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -27,9 +27,10 @@ export interface IUrlEncodedField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract request options - platform agnostic
|
* Core request options - unified interface for all implementations
|
||||||
*/
|
*/
|
||||||
export interface IAbstractRequestOptions {
|
export interface ICoreRequestOptions {
|
||||||
|
// Common options
|
||||||
method?: THttpMethod | string; // Allow string for compatibility
|
method?: THttpMethod | string; // Allow string for compatibility
|
||||||
headers?: any; // Allow any for platform compatibility
|
headers?: any; // Allow any for platform compatibility
|
||||||
keepAlive?: boolean;
|
keepAlive?: boolean;
|
||||||
@@ -37,22 +38,39 @@ export interface IAbstractRequestOptions {
|
|||||||
queryParams?: { [key: string]: string };
|
queryParams?: { [key: string]: string };
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
hardDataCuttingTimeout?: number;
|
hardDataCuttingTimeout?: number;
|
||||||
|
|
||||||
|
// Node.js specific options (ignored in fetch implementation)
|
||||||
|
agent?: any;
|
||||||
|
socketPath?: string;
|
||||||
|
hostname?: string;
|
||||||
|
port?: number;
|
||||||
|
path?: string;
|
||||||
|
|
||||||
|
// Fetch API specific options (ignored in Node.js implementation)
|
||||||
|
credentials?: RequestCredentials;
|
||||||
|
mode?: RequestMode;
|
||||||
|
cache?: RequestCache;
|
||||||
|
redirect?: RequestRedirect;
|
||||||
|
referrer?: string;
|
||||||
|
referrerPolicy?: ReferrerPolicy;
|
||||||
|
integrity?: string;
|
||||||
|
signal?: AbortSignal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract response headers - platform agnostic
|
* Response headers - platform agnostic
|
||||||
*/
|
*/
|
||||||
export type AbstractHeaders = Record<string, string | string[]>;
|
export type Headers = Record<string, string | string[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract response interface - platform agnostic
|
* Core response interface - platform agnostic
|
||||||
*/
|
*/
|
||||||
export interface IAbstractResponse<T = any> {
|
export interface ICoreResponse<T = any> {
|
||||||
// Properties
|
// Properties
|
||||||
ok: boolean;
|
ok: boolean;
|
||||||
status: number;
|
status: number;
|
||||||
statusText: string;
|
statusText: string;
|
||||||
headers: AbstractHeaders;
|
headers: Headers;
|
||||||
url: string;
|
url: string;
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
|
@@ -8,6 +8,11 @@ import { CoreRequest as AbstractCoreRequest } from '../core_base/request.js';
|
|||||||
export class CoreRequest extends AbstractCoreRequest<types.ICoreRequestOptions, CoreResponse> {
|
export class CoreRequest extends AbstractCoreRequest<types.ICoreRequestOptions, CoreResponse> {
|
||||||
constructor(url: string, options: types.ICoreRequestOptions = {}) {
|
constructor(url: string, options: types.ICoreRequestOptions = {}) {
|
||||||
super(url, options);
|
super(url, options);
|
||||||
|
|
||||||
|
// Check for unsupported Node.js-specific options
|
||||||
|
if (options.agent || options.socketPath) {
|
||||||
|
throw new Error('Node.js specific options (agent, socketPath) are not supported in browser/fetch implementation');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -4,7 +4,7 @@ import { CoreResponse as AbstractCoreResponse } from '../core_base/response.js';
|
|||||||
/**
|
/**
|
||||||
* Fetch-based implementation of Core Response class
|
* Fetch-based implementation of Core Response class
|
||||||
*/
|
*/
|
||||||
export class CoreResponse<T = any> extends AbstractCoreResponse<T> implements types.ICoreResponse<T> {
|
export class CoreResponse<T = any> extends AbstractCoreResponse<T> implements types.IFetchResponse<T> {
|
||||||
private response: Response;
|
private response: Response;
|
||||||
private responseClone: Response;
|
private responseClone: Response;
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ export class CoreResponse<T = any> extends AbstractCoreResponse<T> implements ty
|
|||||||
public readonly ok: boolean;
|
public readonly ok: boolean;
|
||||||
public readonly status: number;
|
public readonly status: number;
|
||||||
public readonly statusText: string;
|
public readonly statusText: string;
|
||||||
public readonly headers: types.AbstractHeaders;
|
public readonly headers: types.Headers;
|
||||||
public readonly url: string;
|
public readonly url: string;
|
||||||
|
|
||||||
constructor(response: Response) {
|
constructor(response: Response) {
|
||||||
|
@@ -4,24 +4,12 @@ import * as baseTypes from '../core_base/types.js';
|
|||||||
export * from '../core_base/types.js';
|
export * from '../core_base/types.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Core request options for fetch-based implementation
|
* Fetch-specific response extensions
|
||||||
* Extends RequestInit from the Fetch API
|
|
||||||
*/
|
*/
|
||||||
export interface ICoreRequestOptions extends RequestInit {
|
export interface IFetchResponse<T = any> extends baseTypes.ICoreResponse<T> {
|
||||||
// Override method to be more specific
|
// Fetch-specific methods
|
||||||
method?: baseTypes.THttpMethod;
|
stream(): ReadableStream<Uint8Array> | null;
|
||||||
// Additional options not in RequestInit
|
|
||||||
requestBody?: any;
|
// Access to raw Response object
|
||||||
queryParams?: { [key: string]: string };
|
raw(): Response;
|
||||||
timeout?: number;
|
|
||||||
hardDataCuttingTimeout?: number;
|
|
||||||
// keepAlive maps to keepalive in RequestInit
|
|
||||||
keepAlive?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Core response object for fetch implementation
|
|
||||||
*/
|
|
||||||
export interface ICoreResponse<T = any> extends baseTypes.IAbstractResponse<T> {
|
|
||||||
// Fetch-specific properties (all from base interface)
|
|
||||||
}
|
}
|
@@ -39,6 +39,12 @@ export class CoreRequest extends AbstractCoreRequest<types.ICoreRequestOptions,
|
|||||||
) {
|
) {
|
||||||
super(url, options);
|
super(url, options);
|
||||||
this.requestDataFunc = requestDataFunc;
|
this.requestDataFunc = requestDataFunc;
|
||||||
|
|
||||||
|
// Check for unsupported fetch-specific options
|
||||||
|
if (options.credentials || options.mode || options.cache || options.redirect ||
|
||||||
|
options.referrer || options.referrerPolicy || options.integrity) {
|
||||||
|
throw new Error('Fetch API specific options (credentials, mode, cache, redirect, referrer, referrerPolicy, integrity) are not supported in Node.js implementation');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -5,7 +5,7 @@ import { CoreResponse as AbstractCoreResponse } from '../core_base/response.js';
|
|||||||
/**
|
/**
|
||||||
* Node.js implementation of 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<T = any> extends AbstractCoreResponse<T> implements types.ICoreResponse<T> {
|
export class CoreResponse<T = any> extends AbstractCoreResponse<T> implements types.INodeResponse<T> {
|
||||||
private incomingMessage: plugins.http.IncomingMessage;
|
private incomingMessage: plugins.http.IncomingMessage;
|
||||||
private bodyBufferPromise: Promise<Buffer> | null = null;
|
private bodyBufferPromise: Promise<Buffer> | null = null;
|
||||||
|
|
||||||
|
@@ -4,17 +4,6 @@ import * as baseTypes from '../core_base/types.js';
|
|||||||
// Re-export base types
|
// Re-export base types
|
||||||
export * from '../core_base/types.js';
|
export * from '../core_base/types.js';
|
||||||
|
|
||||||
/**
|
|
||||||
* Core request options extending Node.js RequestOptions
|
|
||||||
* Node.js RequestOptions already includes method and headers
|
|
||||||
*/
|
|
||||||
export interface ICoreRequestOptions extends plugins.https.RequestOptions {
|
|
||||||
keepAlive?: boolean;
|
|
||||||
requestBody?: any;
|
|
||||||
queryParams?: { [key: string]: string };
|
|
||||||
hardDataCuttingTimeout?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extended IncomingMessage with body property (legacy compatibility)
|
* Extended IncomingMessage with body property (legacy compatibility)
|
||||||
*/
|
*/
|
||||||
@@ -23,20 +12,10 @@ export interface IExtendedIncomingMessage<T = any> extends plugins.http.Incoming
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Core response object that provides fetch-like API with Node.js specific methods
|
* Node.js specific response extensions
|
||||||
*/
|
*/
|
||||||
export interface ICoreResponse<T = any> extends baseTypes.IAbstractResponse<T> {
|
export interface INodeResponse<T = any> extends baseTypes.ICoreResponse<T> {
|
||||||
// Properties
|
// Node.js specific methods
|
||||||
ok: boolean;
|
|
||||||
status: number;
|
|
||||||
statusText: string;
|
|
||||||
headers: plugins.http.IncomingHttpHeaders;
|
|
||||||
url: string;
|
|
||||||
|
|
||||||
// Methods
|
|
||||||
json(): Promise<T>;
|
|
||||||
text(): Promise<string>;
|
|
||||||
arrayBuffer(): Promise<ArrayBuffer>;
|
|
||||||
stream(): NodeJS.ReadableStream;
|
stream(): NodeJS.ReadableStream;
|
||||||
|
|
||||||
// Legacy compatibility
|
// Legacy compatibility
|
||||||
|
@@ -3,7 +3,7 @@ export * from './client/index.js';
|
|||||||
|
|
||||||
// Core exports for advanced usage
|
// Core exports for advanced usage
|
||||||
export { CoreResponse } from './core/index.js';
|
export { CoreResponse } from './core/index.js';
|
||||||
export type { IAbstractRequestOptions, IAbstractResponse } from './core_base/types.js';
|
export type { ICoreRequestOptions, ICoreResponse } from './core_base/types.js';
|
||||||
|
|
||||||
// Default export for easier importing
|
// Default export for easier importing
|
||||||
import { SmartRequestClient } from './client/smartrequestclient.js';
|
import { SmartRequestClient } from './client/smartrequestclient.js';
|
||||||
|
Reference in New Issue
Block a user