update
This commit is contained in:
@@ -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();
|
@@ -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;
|
@@ -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>;
|
||||
|
12
ts/index.ts
12
ts/index.ts
@@ -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;
|
@@ -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;
|
||||
}
|
@@ -1,2 +0,0 @@
|
||||
// Export everything from the legacy adapter
|
||||
export * from './adapter.js';
|
Reference in New Issue
Block a user