diff --git a/test/test.legacy.ts b/test/test.legacy.ts deleted file mode 100644 index 083bf83..0000000 --- a/test/test.legacy.ts +++ /dev/null @@ -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(); diff --git a/ts/modern/features/pagination.ts b/ts/client/features/pagination.ts similarity index 100% rename from ts/modern/features/pagination.ts rename to ts/client/features/pagination.ts diff --git a/ts/modern/index.ts b/ts/client/index.ts similarity index 100% rename from ts/modern/index.ts rename to ts/client/index.ts diff --git a/ts/modern/smartrequestclient.ts b/ts/client/smartrequestclient.ts similarity index 97% rename from ts/modern/smartrequestclient.ts rename to ts/client/smartrequestclient.ts index 9616603..4af085f 100644 --- a/ts/modern/smartrequestclient.ts +++ b/ts/client/smartrequestclient.ts @@ -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 { private _url: string; - private _options: ICoreRequestOptions = {}; + private _options: IAbstractRequestOptions = {}; private _retries: number = 0; private _queryParams: Record = {}; private _paginationConfig?: TPaginationConfig; diff --git a/ts/modern/types/common.ts b/ts/client/types/common.ts similarity index 100% rename from ts/modern/types/common.ts rename to ts/client/types/common.ts diff --git a/ts/modern/types/pagination.ts b/ts/client/types/pagination.ts similarity index 100% rename from ts/modern/types/pagination.ts rename to ts/client/types/pagination.ts diff --git a/ts/core/index.ts b/ts/core/index.ts index dde6cd5..1549ca1 100644 --- a/ts/core/index.ts +++ b/ts/core/index.ts @@ -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('../core_node/index.js'); - usedImplementation = nodeImpl as IImplementation; + return smartenvInstance.getSafeNodeModule('../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 = InstanceType; diff --git a/ts/index.ts b/ts/index.ts index 052ee09..8702738 100644 --- a/ts/index.ts +++ b/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; \ No newline at end of file diff --git a/ts/legacy/adapter.ts b/ts/legacy/adapter.ts deleted file mode 100644 index 35abd55..0000000 --- a/ts/legacy/adapter.ts +++ /dev/null @@ -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 { - const done = smartpromise.defer(); - 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 { - 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 { - 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 { - optionsArg.method = 'GET'; - const response = await request(urlArg, optionsArg, true); - return response; -} \ No newline at end of file diff --git a/ts/legacy/index.ts b/ts/legacy/index.ts deleted file mode 100644 index cee4504..0000000 --- a/ts/legacy/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Export everything from the legacy adapter -export * from './adapter.js';