This commit is contained in:
2025-07-28 17:23:48 +00:00
parent 94bf23ad55
commit bc99aa3569
10 changed files with 19 additions and 321 deletions

View File

@@ -1,45 +0,0 @@
import { tap, expect, expectAsync } from '@pushrocks/tapbundle';
import * as smartrequest from '../ts/legacy/index.js';
tap.test('should request a html document over https', async () => {
await expectAsync(smartrequest.getJson('https://encrypted.google.com/')).toHaveProperty('body');
});
tap.test('should request a JSON document over https', async () => {
await expectAsync(smartrequest.getJson('https://jsonplaceholder.typicode.com/posts/1'))
.property('body')
.property('id')
.toEqual(1);
});
tap.test('should post a JSON document over http', async () => {
const testData = { text: 'example_text' };
await expectAsync(smartrequest.postJson('https://httpbin.org/post', { requestBody: testData }))
.property('body')
.property('json')
.property('text')
.toEqual('example_text');
});
tap.test('should safe get stuff', async () => {
smartrequest.safeGet('http://coffee.link/');
smartrequest.safeGet('https://coffee.link/');
});
tap.skip.test('should deal with unix socks', async () => {
const socketResponse = await smartrequest.request(
'http://unix:/var/run/docker.sock:/containers/json',
{
headers: {
'Content-Type': 'application/json',
Host: 'docker.sock',
},
}
);
console.log(socketResponse.body);
});
tap.skip.test('should correctly upload a file using formData', async () => {});
tap.start();

View File

@@ -1,5 +1,6 @@
import { CoreRequest, CoreResponse, type ICoreRequestOptions } from '../core_node/index.js';
import { CoreRequest, CoreResponse } from '../core/index.js';
import * as plugins from '../core_node/plugins.js';
import type { IAbstractRequestOptions } from '../core_base/types.js';
import type { HttpMethod, ResponseType, FormField } from './types/common.js';
import {
@@ -17,7 +18,7 @@ import { createPaginatedResponse } from './features/pagination.js';
*/
export class SmartRequestClient<T = any> {
private _url: string;
private _options: ICoreRequestOptions = {};
private _options: IAbstractRequestOptions = {};
private _retries: number = 0;
private _queryParams: Record<string, string> = {};
private _paginationConfig?: TPaginationConfig;

View File

@@ -1,33 +1,22 @@
import * as smartenv from '@push.rocks/smartenv';
// Re-export base types that are common to all implementations
// Export all base types - these are the public API
export * from '../core_base/types.js';
// Define the implementation interface
interface IImplementation {
CoreRequest: any;
CoreResponse: any;
isUnixSocket?: (url: string) => boolean;
parseUnixSocketUrl?: (url: string) => { socketPath: string; path: string };
// Type exports
ICoreRequestOptions?: any;
ICoreResponse?: any;
IExtendedIncomingMessage?: any;
}
let usedImplementation: IImplementation;
const smartenvInstance = new smartenv.Smartenv();
// Initialize the implementation based on environment
const initPromise = (async () => {
// Load the appropriate implementation based on environment
const implementation = await (async () => {
if (smartenvInstance.isNode) {
const nodeImpl = await smartenvInstance.getSafeNodeModule<typeof import('../core_node/index.js')>('../core_node/index.js');
usedImplementation = nodeImpl as IImplementation;
return smartenvInstance.getSafeNodeModule<typeof import('../core_node/index.js')>('../core_node/index.js');
} else {
const fetchImpl = await import('../core_fetch/index.js');
usedImplementation = fetchImpl as IImplementation;
return import('../core_fetch/index.js');
}
})();
// Export a function to ensure implementation is loaded
export const ensureImplementationLoaded = () => initPromise;
// Export the implementation classes
export const CoreRequest = implementation.CoreRequest;
export const CoreResponse = implementation.CoreResponse;
// Export CoreResponse as a type for type annotations
export type CoreResponse<T = any> = InstanceType<typeof implementation.CoreResponse>;

View File

@@ -1,12 +1,10 @@
// Legacy API exports (for backward compatibility)
export * from './legacy/index.js';
// Modern API exports
export * from './modern/index.js';
// Client API exports
export * from './client/index.js';
// Core exports for advanced usage
export { CoreResponse, type ICoreRequestOptions, type ICoreResponse } from './core_node/index.js';
export { CoreResponse } from './core/index.js';
export type { IAbstractRequestOptions, IAbstractResponse } from './core_base/types.js';
// Default export for easier importing
import { SmartRequestClient } from './modern/smartrequestclient.js';
import { SmartRequestClient } from './client/smartrequestclient.js';
export default SmartRequestClient;

View File

@@ -1,243 +0,0 @@
/**
* Legacy adapter that provides backward compatibility
* Maps legacy API to the new core module
*/
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_node/types.js';
export interface ISmartRequestOptions extends core.ICoreRequestOptions {
autoJsonParse?: boolean;
responseType?: 'json' | 'text' | 'binary' | 'stream';
}
// Re-export interface for form fields
export interface IFormField {
name: string;
type: 'string' | 'filePath' | 'Buffer';
payload: string | Buffer;
fileName?: string;
contentType?: string;
}
/**
* Helper function to convert stream to IExtendedIncomingMessage for legacy compatibility
*/
async function streamToExtendedMessage(
stream: plugins.http.IncomingMessage,
autoJsonParse = true
): Promise<core.IExtendedIncomingMessage> {
const done = smartpromise.defer<core.IExtendedIncomingMessage>();
const chunks: Buffer[] = [];
stream.on('data', (chunk: Buffer) => {
chunks.push(chunk);
});
stream.on('end', () => {
const buffer = Buffer.concat(chunks);
const extendedMessage = stream as core.IExtendedIncomingMessage;
if (autoJsonParse) {
const text = buffer.toString('utf-8');
try {
extendedMessage.body = JSON.parse(text);
} catch (err) {
extendedMessage.body = text;
}
} else {
extendedMessage.body = buffer;
}
done.resolve(extendedMessage);
});
stream.on('error', (err) => {
done.reject(err);
});
return done.promise;
}
/**
* Legacy request function that returns IExtendedIncomingMessage
*/
export async function request(
urlArg: string,
optionsArg: ISmartRequestOptions = {},
responseStreamArg = false,
requestDataFunc?: (req: plugins.http.ClientRequest) => void
): Promise<core.IExtendedIncomingMessage> {
const coreRequest = new core.CoreRequest(urlArg, optionsArg, requestDataFunc);
const stream = await coreRequest.fireCore();
if (responseStreamArg) {
// For stream responses, just cast and return
return stream as core.IExtendedIncomingMessage;
}
// Convert stream to IExtendedIncomingMessage
const autoJsonParse = optionsArg.autoJsonParse !== false;
return streamToExtendedMessage(stream, autoJsonParse);
}
/**
* Safe GET request
*/
export async function safeGet(urlArg: string): Promise<core.IExtendedIncomingMessage | null> {
const agentToUse = urlArg.startsWith('http://')
? new plugins.http.Agent()
: new plugins.https.Agent();
try {
const response = await request(urlArg, {
method: 'GET',
agent: agentToUse,
timeout: 5000,
hardDataCuttingTimeout: 5000,
autoJsonParse: false,
});
return response;
} catch (err) {
console.error(err);
return null;
}
}
/**
* GET JSON request
*/
export async function getJson(urlArg: string, optionsArg: ISmartRequestOptions = {}) {
optionsArg.method = 'GET';
return request(urlArg, optionsArg);
}
/**
* POST JSON request
*/
export async function postJson(urlArg: string, optionsArg: ISmartRequestOptions = {}) {
optionsArg.method = 'POST';
if (
typeof optionsArg.requestBody === 'object' &&
(!optionsArg.headers || !optionsArg.headers['Content-Type'])
) {
// make sure headers exist
if (!optionsArg.headers) {
optionsArg.headers = {};
}
// assign the right Content-Type, leaving all other headers in place
optionsArg.headers = {
...optionsArg.headers,
'Content-Type': 'application/json',
};
}
return request(urlArg, optionsArg);
}
/**
* PUT JSON request
*/
export async function putJson(urlArg: string, optionsArg: ISmartRequestOptions = {}) {
optionsArg.method = 'PUT';
return request(urlArg, optionsArg);
}
/**
* DELETE JSON request
*/
export async function delJson(urlArg: string, optionsArg: ISmartRequestOptions = {}) {
optionsArg.method = 'DELETE';
return request(urlArg, optionsArg);
}
/**
* GET binary data
*/
export async function getBinary(urlArg: string, optionsArg: ISmartRequestOptions = {}) {
optionsArg = {
...optionsArg,
autoJsonParse: false,
responseType: 'binary'
};
return request(urlArg, optionsArg);
}
/**
* POST form data
*/
export async function postFormData(urlArg: string, formFields: IFormField[], optionsArg: ISmartRequestOptions = {}) {
const form = new plugins.formData();
for (const formField of formFields) {
if (formField.type === 'filePath') {
const fileData = plugins.fs.readFileSync(
plugins.path.isAbsolute(formField.payload as string)
? formField.payload as string
: plugins.path.join(process.cwd(), formField.payload as string)
);
form.append(formField.name, fileData, {
filename: formField.fileName || plugins.path.basename(formField.payload as string),
contentType: formField.contentType
});
} else if (formField.type === 'Buffer') {
form.append(formField.name, formField.payload, {
filename: formField.fileName,
contentType: formField.contentType
});
} else {
form.append(formField.name, formField.payload);
}
}
optionsArg.method = 'POST';
optionsArg.requestBody = form;
if (!optionsArg.headers) {
optionsArg.headers = {};
}
optionsArg.headers = {
...optionsArg.headers,
...form.getHeaders()
};
return request(urlArg, optionsArg);
}
/**
* POST URL encoded form data
*/
export async function postFormDataUrlEncoded(
urlArg: string,
formFields: { key: string; content: string }[],
optionsArg: ISmartRequestOptions = {}
) {
optionsArg.method = 'POST';
if (!optionsArg.headers) {
optionsArg.headers = {};
}
optionsArg.headers['Content-Type'] = 'application/x-www-form-urlencoded';
const urlEncodedBody = formFields
.map(field => `${encodeURIComponent(field.key)}=${encodeURIComponent(field.content)}`)
.join('&');
optionsArg.requestBody = urlEncodedBody;
return request(urlArg, optionsArg);
}
/**
* GET stream
*/
export async function getStream(
urlArg: string,
optionsArg: ISmartRequestOptions = {}
): Promise<plugins.http.IncomingMessage> {
optionsArg.method = 'GET';
const response = await request(urlArg, optionsArg, true);
return response;
}

View File

@@ -1,2 +0,0 @@
// Export everything from the legacy adapter
export * from './adapter.js';