@autofleet/rapido-http-client 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/testing.cjs +1 -1
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/dist/{utils-B2KjeMp7.js → utils-CN26OYva.js} +1 -1
- package/dist/{utils-B2KjeMp7.js.map → utils-CN26OYva.js.map} +1 -1
- package/dist/{utils-B1ZhSXeE.cjs → utils-Dh5SqGdK.cjs} +1 -1
- package/dist/{utils-B1ZhSXeE.cjs.map → utils-Dh5SqGdK.cjs.map} +1 -1
- package/package.json +6 -5
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,`__esModule`,{value:!0});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));const c=require(`./utils-
|
|
1
|
+
Object.defineProperty(exports,`__esModule`,{value:!0});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));const c=require(`./utils-Dh5SqGdK.cjs`);let l=require(`node:http`),u=require(`undici`);u=s(u);let d=require(`@autofleet/outbreak`);const f=Symbol.for(`RapidoHttpClientErrorBrand`);let p;(function(e){e.UndiciError=u.errors.UndiciError,e.ConnectTimeoutError=u.errors.ConnectTimeoutError,e.HeadersTimeoutError=u.errors.HeadersTimeoutError,e.HeadersOverflowError=u.errors.HeadersOverflowError,e.BodyTimeoutError=u.errors.BodyTimeoutError,e.ResponseError=u.errors.ResponseError,e.InvalidArgumentError=u.errors.InvalidArgumentError,e.InvalidReturnValueError=u.errors.InvalidReturnValueError,e.RequestAbortedError=u.errors.RequestAbortedError,e.InformationalError=u.errors.InformationalError,e.RequestContentLengthMismatchError=u.errors.RequestContentLengthMismatchError,e.ResponseContentLengthMismatchError=u.errors.ResponseContentLengthMismatchError,e.ClientDestroyedError=u.errors.ClientDestroyedError,e.ClientClosedError=u.errors.ClientClosedError,e.SocketError=u.errors.SocketError,e.NotSupportedError=u.errors.NotSupportedError,e.BalancedPoolMissingUpstreamError=u.errors.BalancedPoolMissingUpstreamError,e.HTTPParserError=u.errors.HTTPParserError,e.ResponseExceededMaxSizeError=u.errors.ResponseExceededMaxSizeError,e.RequestRetryError=u.errors.RequestRetryError,e.SecureProxyConnectionError=u.errors.SecureProxyConnectionError,e.MaxOriginsReachedError=u.errors.MaxOriginsReachedError;function t(e){return typeof e==`object`&&!!e&&f in e}e.isRapidoHttpClientError=t;class n extends u.errors.ResponseError{constructor(e,t,n){super(`HTTP Error ${t.statusCode}`,t.statusCode,{headers:t.headers,body:e}),this.response=t,this.data=n,this.status=t.statusCode,this.statusText=l.STATUS_CODES[t.statusCode]||`Unknown Status`,Object.defineProperty(this,f,{value:!0,enumerable:!1,writable:!1})}static isRapidoHttpClientError(e){return t(e)}}e.RapidoHttpClientError=n})(p||={});const m=Symbol(`testAgentParam`),h=e=>function(t,n){let r=(0,d.getCurrentContext)().context;if(r){let e;e=Array.isArray(t.headers)?Object.fromEntries(t.headers):{...t.headers};for(let[t,n]of r)typeof n==`symbol`||n===void 0||(e[t]=n);t.headers=e}return e(t,n)},{cacheStores:g}=u.default,{cache:_,retry:v,decompress:y}=u.interceptors,b=[`GET`,`HEAD`,`OPTIONS`,`PUT`,`DELETE`,`TRACE`,`QUERY`];var x=class{#e;get(e,t){return this.#i(`GET`,e,void 0,t)}post(e,t,n){return this.#i(`POST`,e,t,n)}delete(e,t){return this.#i(`DELETE`,e,void 0,t)}head(e,t){return this.#i(`HEAD`,e,void 0,t)}put(e,t,n){return this.#i(`PUT`,e,t,n)}patch(e,t,n){return this.#i(`PATCH`,e,t,n)}options(e,t){return this.#i(`OPTIONS`,e,void 0,t)}query(e,t,n){return this.#i(`QUERY`,e,t,n)}constructor(e){this.#e=e.logger,this.settings=c.o(e.autoDecompress?c.i:c.r,e),this.#t();let t=globalThis.__RAPIDO_HTTP_CLIENT_MOCK_AGENT__;this.client=this.settings[m]??t??new u.Agent({connect:{timeout:this.settings.timeout},keepAliveTimeout:this.settings.keepAliveTimeout,keepAliveMaxTimeout:this.settings.keepAliveMaxTimeout,connections:this.settings.connections}),this.settings.cache&&(this.client=this.client.compose(_({store:new g.MemoryCacheStore({...this.settings.cache})}))),this.settings.retries&&(this.client=this.client.compose(v({methods:b,...this.settings.retries,retry:(e,t,n)=>{let{counter:r}=t.state,i=t.opts.retryOptions?.maxRetries??5;(this.settings.retries?.retry??c.n)(e,t,a=>{if(!a){let{method:n,origin:a,path:o}=t.opts,s=!a||typeof a==`string`?a:a.origin;this.#e.warn(`Request retry attempt ${r}/${i}: ${c.t(n,s,o)}`,{err:e})}n(a)})}}))),this.settings.autoDecompress&&(this.client=this.client.compose(y({skipErrorResponses:!1}))),this.client=this.client.compose(h)}#t(){let e=c.c(this.settings),t=new URL(e);this.settings.baseURL=t.origin,this.settings.basePath=t.pathname===`/`?``:t.pathname}#n(e,t){if(!e&&!t)return{signal:void 0,timeoutSignal:void 0};if(e&&!t)return{signal:e,timeoutSignal:void 0};let n=AbortSignal.timeout(t);return!e&&t?{signal:n,timeoutSignal:n}:{signal:AbortSignal.any([e,n]),timeoutSignal:n}}#r(e,t){if(e!=null){if(c.a(e))return!t[`Content-Type`]&&!t[`content-type`]&&(t[`Content-Type`]=`application/x-www-form-urlencoded`),e;if(typeof e==`object`&&!Buffer.isBuffer(e)){let n=t[`Content-Type`]||t[`content-type`];if(n&&/application\/x-www-form-urlencoded/i.test(n)){let t=new URLSearchParams;for(let[n,r]of Object.entries(e))Array.isArray(r)?r.forEach(e=>t.append(n,String(e))):r!=null&&t.append(n,String(r));return t.toString()}return!t[`Content-Type`]&&!t[`content-type`]&&(t[`Content-Type`]=`application/json`),JSON.stringify(e)}else if(typeof e==`string`)return e;else if(Buffer.isBuffer(e))return e;return String(e)}}async#i(e,t,n,r={}){let i=c.o(this.settings,r);if(i.signal?.aborted)throw this.#e.error(`Request aborted before start: ${c.t(e,i.baseURL,t)}`),new p.RequestAbortedError(i.signal.reason?.message||`Request aborted`,{cause:i.signal.reason});let a={...i.headers},o=this.#r(n,a),{origin:s,path:u}=c.s(t,i.baseURL,i.basePath),d={method:e,baseURL:s,url:t};if(i.params)for(let[e,t]of Object.entries(i.params))Array.isArray(t)&&!e.endsWith(`[]`)&&(i.params[`${e}[]`]=t,delete i.params[e]);let{signal:f,timeoutSignal:m}=this.#n(i.signal,i.timeout);try{this.#e.info(`Start Request: ${c.t(e,s,u)}`);let t={path:u,origin:s,method:e,signal:f,headers:a,body:o,query:i.params},n=await this.client.request(t);if(!(i.validateStatus??(()=>n.statusCode<400))(n.statusCode)){let t=await this.#a(n,r.responseSchema);throw this.#e.error(`Finish Request with error ${c.t(e,s,u)}`,{status:n.statusCode,data:t}),new p.RapidoHttpClientError(o,n,t)}return this.#e.info(`Finish Request: ${c.t(e,s,u)}`),{data:n.statusCode===204?void 0:await this.#a(n,r.responseSchema),status:n.statusCode,statusText:l.STATUS_CODES[n.statusCode]||``,headers:n.headers,config:d}}catch(t){if(m&&m.reason===t){this.#e.error(`Request timeout: ${c.t(e,s,u)}`);let t=new p.UndiciError(`Request timeout after ${i.timeout}ms`,{cause:m.reason});throw t.code=`UND_ERR_TIMEOUT`,t}throw f&&f.reason===t?(this.#e.error(`Request aborted: ${c.t(e,s,u)}`),new p.RequestAbortedError(f.reason.message,{cause:f.reason})):(this.#e.error(`Finish Request with error ${c.t(e,s,u)}`,{error:t}),t)}}async#a(e,t){let n=e.headers[`content-type`]||``,r=await e.body.text();if(!r)return;if(/text/i.test(n))return r;let i;try{i=JSON.parse(r)}catch{return r}return t?t.parse(i):i}async getAllPages(e,t={}){let n=[],r=null,i=null,a={...t,params:{...t.params,page:1}};for(a.params.page||=1;(a.params.page===1||i===r)&&i!==0;){let{data:t}=await this.get(e,a);i=t.length,a.params.page===1&&(r=t.length),a.params.page+=1,Array.prototype.push.apply(n,t)}return n}async getAllPagesFromQueryEndpoint(e,t={},n=100){let r=[],i=!0,a=1;for(;i&&(n===-1||a<n);){let{data:n}=await this.post(e,{...t,page:a}),{rows:o,count:s}=n;a+=1,Array.prototype.push.apply(r,o),i=r.length<s}return r}async close(){await this.client.close()}async[Symbol.asyncDispose](){await this.close()}},S=x;exports.RapidoHttpClient=x,exports.default=S,Object.defineProperty(exports,`errors`,{enumerable:!0,get:function(){return p}}),exports.t=s;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["undiciErrors","response: Dispatcher.ResponseData","data: T","testAgentParam: unique symbol","outbreakInterceptor: Dispatcher.DispatcherComposeInterceptor","headers: Record<string, string | string[]>","Undici","interceptors","retriableMethods: Dispatcher.HttpMethod[]","#logger","#request","mergeConfig","defaultSettingsWithDecompress","defaultSettings","#createBaseUrl","Agent","defaultRetryMethod","resolveServiceUrl","isFormData","createRequestString","headers: Record<string, string>","#prepareRequestBody","parseUrlAndOrigin","#getSignal","requestOptions: Dispatcher.RequestOptions","#parseResponse","error","json: unknown","currentResult: T[]","resultsInFirstPage: number | null","lastResultsSize: number | null"],"sources":["../src/errors.ts","../src/testingHelper.ts","../src/outbreakInterceptor.ts","../src/index.ts"],"sourcesContent":["import { type Dispatcher, errors as undiciErrors } from 'undici';\n\nconst RapidoHttpClientErrorBrand = Symbol.for('RapidoHttpClientErrorBrand');\n\n// eslint-disable-next-line @typescript-eslint/no-namespace\nexport namespace errors {\n export import UndiciError = undiciErrors.UndiciError;\n export import ConnectTimeoutError = undiciErrors.ConnectTimeoutError;\n export import HeadersTimeoutError = undiciErrors.HeadersTimeoutError;\n export import HeadersOverflowError = undiciErrors.HeadersOverflowError;\n export import BodyTimeoutError = undiciErrors.BodyTimeoutError;\n export import ResponseError = undiciErrors.ResponseError;\n export import InvalidArgumentError = undiciErrors.InvalidArgumentError;\n export import InvalidReturnValueError = undiciErrors.InvalidReturnValueError;\n export import RequestAbortedError = undiciErrors.RequestAbortedError;\n export import InformationalError = undiciErrors.InformationalError;\n export import RequestContentLengthMismatchError = undiciErrors.RequestContentLengthMismatchError;\n export import ResponseContentLengthMismatchError = undiciErrors.ResponseContentLengthMismatchError;\n export import ClientDestroyedError = undiciErrors.ClientDestroyedError;\n export import ClientClosedError = undiciErrors.ClientClosedError;\n export import SocketError = undiciErrors.SocketError;\n export import NotSupportedError = undiciErrors.NotSupportedError;\n export import BalancedPoolMissingUpstreamError = undiciErrors.BalancedPoolMissingUpstreamError;\n export import HTTPParserError = undiciErrors.HTTPParserError;\n export import ResponseExceededMaxSizeError = undiciErrors.ResponseExceededMaxSizeError;\n export import RequestRetryError = undiciErrors.RequestRetryError;\n export import SecureProxyConnectionError = undiciErrors.SecureProxyConnectionError;\n export import MaxOriginsReachedError = undiciErrors.MaxOriginsReachedError;\n\n export function isRapidoHttpClientError(value: unknown): value is RapidoHttpClientError<unknown> {\n return typeof value === 'object' && value !== null && RapidoHttpClientErrorBrand in value;\n }\n\n export class RapidoHttpClientError<T> extends undiciErrors.ResponseError {\n public readonly status: number;\n\n constructor(\n body: ConstructorParameters<typeof undiciErrors.ResponseError>[2]['body'],\n public readonly response: Dispatcher.ResponseData,\n public readonly data: T,\n ) {\n super(`HTTP Error ${response.statusCode}`, response.statusCode, { headers: response.headers, body });\n this.status = response.statusCode;\n\n Object.defineProperty(this, RapidoHttpClientErrorBrand, {\n value: true,\n enumerable: false,\n writable: false,\n });\n }\n\n public static isRapidoHttpClientError(value: unknown): value is RapidoHttpClientError<unknown> {\n return isRapidoHttpClientError(value);\n }\n }\n}\n","export const testAgentParam: unique symbol = Symbol('testAgentParam');\n","import type { Dispatcher } from 'undici';\nimport { getCurrentContext } from '@autofleet/outbreak';\n\nexport const outbreakInterceptor: Dispatcher.DispatcherComposeInterceptor = dispatch => function outbreakInterceptorInner(opts, handler) {\n const currentTraceContext = getCurrentContext().context;\n if (currentTraceContext) {\n // Convert headers to object format if they're in array format\n let headers: Record<string, string | string[]>;\n if (Array.isArray(opts.headers)) {\n /* c8 ignore next */\n headers = Object.fromEntries(opts.headers as unknown as [string, string | string[]][]);\n } else {\n headers = { ...opts.headers as Record<string, string | string[]> };\n }\n\n // Inject outbreak trace headers\n for (const [header, value] of currentTraceContext) {\n if (typeof value === 'symbol' || typeof value === 'undefined') {\n continue;\n }\n headers[header] = value as string;\n }\n opts.headers = headers;\n }\n return dispatch(opts, handler);\n};\n","import type { ZodType, output } from 'zod';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\nimport type CacheHandler from 'undici/types/cache-interceptor';\nimport Undici, { Agent, type Dispatcher, interceptors } from 'undici';\nimport { errors } from './errors';\nimport { testAgentParam } from './testingHelper';\nimport { outbreakInterceptor } from './outbreakInterceptor';\nimport {\n createRequestString,\n defaultRetryMethod,\n defaultSettings,\n defaultSettingsWithDecompress,\n isFormData,\n mergeConfig,\n parseUrlAndOrigin,\n resolveServiceUrl,\n} from './utils';\n\nconst { cacheStores } = Undici;\nconst { cache, retry, decompress } = interceptors;\n\nexport interface RapidoHttpClientSettings {\n [testAgentParam]?: Dispatcher;\n serviceName?: string;\n serviceUrl?: string;\n logger: LoggerInstanceManager;\n timeout?: number;\n retries?: interceptors.RetryInterceptorOpts;\n headers?: Record<string, string> | string[][];\n keepAliveTimeout?: number;\n keepAliveMaxTimeout?: number;\n connections?: number;\n cache?: CacheHandler.MemoryCacheStoreOpts;\n autoDecompress?: boolean;\n}\n\nexport interface RequestConfig {\n params?: Record<string, any>;\n body?: any;\n timeout?: number;\n headers?: Record<string, string>;\n signal?: AbortSignal;\n responseSchema?: ZodType;\n}\n\nexport interface RapidoHttpClientResponse<T = any> {\n data: T;\n status: number;\n statusText: string;\n headers: Record<string, string | string[]>;\n config: RequestConfig & { url: string; method: string; baseURL?: string; };\n}\n\nconst retriableMethods: Dispatcher.HttpMethod[] = ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE', 'QUERY'];\n\nexport class RapidoHttpClient {\n readonly #logger: LoggerInstanceManager;\n private readonly settings: RapidoHttpClientSettings & { baseURL?: string; basePath?: string; };\n public readonly client: Dispatcher.ComposedDispatcher;\n\n public get<T = unknown>(url: string, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public get<T extends ZodType>(url: string, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public get<T = unknown>(url: string, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('GET', url, undefined, config);\n }\n\n public post<T = unknown>(url: string, body?: any, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public post<T extends ZodType>(url: string, body: any, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public post<T = unknown>(url: string, body?: any, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('POST', url, body, config);\n }\n\n public delete<T = unknown>(url: string, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public delete<T extends ZodType>(url: string, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public delete<T = unknown>(url: string, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('DELETE', url, undefined, config);\n }\n\n public head<T = unknown>(url: string, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public head<T extends ZodType>(url: string, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public head<T = unknown>(url: string, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('HEAD', url, undefined, config);\n }\n\n public put<T = unknown>(url: string, body?: any, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public put<T extends ZodType>(url: string, body: any, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public put<T = unknown>(url: string, body?: any, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('PUT', url, body, config);\n }\n\n public patch<T = unknown>(url: string, body?: any, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public patch<T extends ZodType>(url: string, body: any, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public patch<T = unknown>(url: string, body?: any, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('PATCH', url, body, config);\n }\n\n public options<T = unknown>(url: string, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public options<T extends ZodType>(url: string, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public options<T = unknown>(url: string, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('OPTIONS', url, undefined, config);\n }\n\n public query<T = unknown>(url: string, body?: any, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public query<T extends ZodType>(url: string, body: any, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public query<T = unknown>(url: string, body?: any, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('QUERY', url, body, config);\n }\n\n constructor(settings: RapidoHttpClientSettings) {\n this.#logger = settings.logger;\n this.settings = mergeConfig<typeof defaultSettings, Partial<typeof settings>>(\n settings.autoDecompress ? defaultSettingsWithDecompress : defaultSettings,\n settings,\n ) as RapidoHttpClientSettings & { baseURL?: string; };\n this.#createBaseUrl();\n\n // In test mode, use the test dispatcher directly (MockAgent or MockPool) Otherwise, create an Agent which creates Client Pools\n // Check for global mock agent first (for testing), then test param, then create real Agent\n /* c8 ignore next */\n const globalMockAgent = (globalThis as any).__RAPIDO_HTTP_CLIENT_MOCK_AGENT__;\n /* c8 ignore next */\n this.client = this.settings[testAgentParam] ?? globalMockAgent ?? new Agent({\n connect: {\n timeout: this.settings.timeout,\n },\n keepAliveTimeout: this.settings.keepAliveTimeout,\n keepAliveMaxTimeout: this.settings.keepAliveMaxTimeout,\n connections: this.settings.connections,\n });\n\n if (this.settings.cache) {\n this.client = this.client.compose(cache({\n store: new cacheStores.MemoryCacheStore({\n ...this.settings.cache,\n }),\n }));\n }\n\n if (this.settings.retries) {\n this.client = this.client.compose(retry({\n methods: retriableMethods,\n ...this.settings.retries,\n retry: (err, context, callback) => {\n const { counter } = context.state;\n /* c8 ignore next */\n const maxRetries = context.opts.retryOptions?.maxRetries ?? 5;\n\n const retryToUse = this.settings.retries?.retry ?? defaultRetryMethod;\n const callbackWithLogging = (result?: Error | null) => {\n if (!result) {\n this.#logger.warn(`Request retry attempt ${counter}/${maxRetries}`, { err });\n }\n callback(result);\n };\n retryToUse(err, context, callbackWithLogging);\n },\n }));\n }\n\n if (this.settings.autoDecompress) {\n this.client = this.client.compose(decompress({ skipErrorResponses: false }));\n }\n\n this.client = this.client.compose(outbreakInterceptor);\n }\n\n #createBaseUrl(): void {\n const fullUrl = resolveServiceUrl(this.settings);\n const urlObj = new URL(fullUrl);\n this.settings.baseURL = urlObj.origin;\n this.settings.basePath = urlObj.pathname !== '/' ? urlObj.pathname : '';\n }\n\n #getSignal(currentSignal?: AbortSignal, timeout?: number): { signal?: AbortSignal; timeoutSignal?: AbortSignal; } {\n if (!currentSignal && !timeout) {\n return { signal: undefined, timeoutSignal: undefined };\n }\n\n if (currentSignal && !timeout) {\n return { signal: currentSignal, timeoutSignal: undefined };\n }\n\n const timeoutSignal = AbortSignal.timeout(timeout!);\n if (!currentSignal && timeout) {\n return { signal: timeoutSignal, timeoutSignal };\n }\n\n return { signal: AbortSignal.any([currentSignal!, timeoutSignal]), timeoutSignal };\n }\n\n #prepareRequestBody(body: any, headers: Record<string, string>): string | Buffer | FormData | undefined {\n if (body === undefined || body === null) {\n return undefined;\n }\n\n // Handle FormData instances automatically\n if (isFormData(body)) {\n if (!headers['Content-Type'] && !headers['content-type']) {\n headers['Content-Type'] = 'application/x-www-form-urlencoded';\n }\n return body;\n }\n\n if (typeof body === 'object' && !Buffer.isBuffer(body)) {\n // Check if Content-Type is set to application/x-www-form-urlencoded\n const contentType = headers['Content-Type'] || headers['content-type'];\n if (contentType && /application\\/x-www-form-urlencoded/i.test(contentType)) {\n // Encode as URL parameters\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(body)) {\n if (Array.isArray(value)) {\n value.forEach(v => params.append(key, String(v)));\n } else if (value !== undefined && value !== null) {\n // eslint-disable-next-line @typescript-eslint/no-base-to-string\n params.append(key, String(value));\n }\n }\n return params.toString();\n }\n // Default to JSON\n if (!headers['Content-Type'] && !headers['content-type']) {\n headers['Content-Type'] = 'application/json';\n }\n return JSON.stringify(body);\n } else if (typeof body === 'string') {\n return body;\n } else if (Buffer.isBuffer(body)) {\n return body;\n }\n return String(body);\n }\n\n async #request<T = any>(\n method: Dispatcher.HttpMethod,\n url: string,\n body?: any,\n config: RequestConfig = {},\n ): Promise<RapidoHttpClientResponse<T>> {\n const mergedConfig = mergeConfig(this.settings, config);\n\n if (mergedConfig.signal?.aborted) {\n this.#logger.error(`Request aborted before start: ${createRequestString(method, mergedConfig.baseURL, url)}`);\n throw new errors.RequestAbortedError(mergedConfig.signal.reason?.message || 'Request aborted', { cause: mergedConfig.signal.reason });\n }\n\n const headers: Record<string, string> = { ...mergedConfig.headers };\n const requestBody = this.#prepareRequestBody(body, headers);\n\n const { origin, path } = parseUrlAndOrigin(url, mergedConfig.baseURL!, mergedConfig.basePath);\n const requestInfo = { method, baseURL: origin, url };\n\n if (mergedConfig.params) {\n for (const [key, value] of Object.entries(mergedConfig.params)) {\n if (Array.isArray(value) && !key.endsWith('[]')) {\n mergedConfig.params[`${key}[]`] = value;\n delete mergedConfig.params[key];\n }\n }\n }\n\n const { signal, timeoutSignal } = this.#getSignal(mergedConfig.signal, mergedConfig.timeout);\n\n try {\n this.#logger.info(`Start Request: ${createRequestString(method, origin, path)}`);\n\n const requestOptions: Dispatcher.RequestOptions = {\n path,\n origin,\n method,\n signal,\n headers,\n body: requestBody,\n query: mergedConfig.params,\n };\n\n const response = await this.client.request(requestOptions);\n\n if (response.statusCode >= 400) {\n const data = await this.#parseResponse<T>(response, config.responseSchema);\n this.#logger.error(`Finish Request with error ${createRequestString(method, origin, path)}`, {\n status: response.statusCode,\n data,\n });\n\n throw new errors.RapidoHttpClientError<T>(requestBody, response, data);\n }\n\n this.#logger.info(`Finish Request: ${createRequestString(method, origin, path)}`);\n\n const data = response.statusCode !== 204 ? await this.#parseResponse<T>(response, config.responseSchema) : undefined as T;\n\n return {\n data,\n status: response.statusCode,\n statusText: '',\n headers: response.headers as Record<string, string | string[]>,\n config: requestInfo,\n };\n } catch (error) {\n if (timeoutSignal && timeoutSignal.reason === error) {\n // If the timeoutSignal aborted (config timeout), throw a timeout-specific error\n this.#logger.error(`Request timeout: ${createRequestString(method, origin, path)}`);\n const error = new errors.UndiciError(`Request timeout after ${mergedConfig.timeout}ms`, { cause: timeoutSignal.reason });\n error.code = 'UND_ERR_TIMEOUT';\n throw error;\n }\n if (signal && signal.reason === error) {\n // If the user's signal aborted, throw RequestAbortedError\n this.#logger.error(`Request aborted: ${createRequestString(method, origin, path)}`);\n throw new errors.RequestAbortedError(signal.reason.message, { cause: signal.reason });\n }\n\n this.#logger.error(`Finish Request with error ${createRequestString(method, origin, path)}`, { error });\n throw error;\n }\n }\n\n async #parseResponse<T>(response: Dispatcher.ResponseData, schema?: ZodType): Promise<T> {\n const contentType = (response.headers['content-type'] as string) || '';\n const bodyText = await response.body.text();\n\n if (!bodyText) {\n return undefined as T;\n }\n\n if (/text/i.test(contentType)) {\n return bodyText as T;\n }\n\n let json: unknown;\n try {\n json = JSON.parse(bodyText);\n } catch {\n return bodyText as T;\n }\n return (schema ? schema.parse(json) : json) as T;\n }\n\n /**\n * @param url The endpoint URL to send the request to.\n * @param options Additional options to include in the request payload.\n * @returns A promise that resolves to an array of all results.\n */\n async getAllPages<T>(url: string, options: RequestConfig = {}): Promise<T[]> {\n const currentResult: T[] = [];\n let resultsInFirstPage: number | null = null;\n let lastResultsSize: number | null = null;\n\n const localOptions = { ...options, params: { ...options.params, page: 1 } };\n // Make sure even if `options` had `params` we initialize `page` to 1.\n localOptions.params.page ||= 1;\n\n while ((localOptions.params.page === 1 || lastResultsSize === resultsInFirstPage) && lastResultsSize !== 0) {\n const { data } = await this.get<T[]>(url, localOptions as Omit<RequestConfig, 'responseSchema'>);\n lastResultsSize = data.length;\n if (localOptions.params.page === 1) {\n resultsInFirstPage = data.length;\n }\n\n localOptions.params.page += 1;\n Array.prototype.push.apply(currentResult, data);\n }\n\n return currentResult;\n }\n\n /**\n * Fetches all pages from a paginated API endpoint until all results are retrieved\n * or an optional page limit is reached.\n *\n * @param url The endpoint URL to send the request to.\n * @param options Additional options to include in the request payload.\n * @param pageLimit The maximum number of pages to fetch. Set to -1 for no limit.\n * @returns A promise that resolves to an array of all results.\n */\n async getAllPagesFromQueryEndpoint<T>(url: string, options: object = {}, pageLimit = 100): Promise<T[]> {\n const currentResult: T[] = [];\n\n let moreResultsToLoad = true;\n let page = 1;\n while (moreResultsToLoad && (pageLimit === -1 || page < pageLimit)) {\n const { data } = await this.post<{ rows: T[]; count: number; }>(url, {\n ...options,\n page,\n });\n const { rows, count } = data;\n page += 1;\n Array.prototype.push.apply(currentResult, rows);\n moreResultsToLoad = currentResult.length < count;\n }\n\n return currentResult;\n }\n\n async close(): Promise<void> {\n await this.client.close();\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.close();\n }\n}\n\nexport default RapidoHttpClient;\nexport { errors };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["undiciErrors","response: Dispatcher.ResponseData","data: T","STATUS_CODES","testAgentParam: unique symbol","outbreakInterceptor: Dispatcher.DispatcherComposeInterceptor","headers: Record<string, string | string[]>","Undici","interceptors","retriableMethods: Dispatcher.HttpMethod[]","#logger","#request","mergeConfig","defaultSettingsWithDecompress","defaultSettings","#createBaseUrl","Agent","defaultRetryMethod","createRequestString","resolveServiceUrl","isFormData","headers: Record<string, string>","#prepareRequestBody","parseUrlAndOrigin","#getSignal","requestOptions: Dispatcher.RequestOptions","#parseResponse","STATUS_CODES","error","json: unknown","currentResult: T[]","resultsInFirstPage: number | null","lastResultsSize: number | null"],"sources":["../src/errors.ts","../src/testingHelper.ts","../src/outbreakInterceptor.ts","../src/index.ts"],"sourcesContent":["import { STATUS_CODES } from 'node:http';\nimport { type Dispatcher, errors as undiciErrors } from 'undici';\n\nconst RapidoHttpClientErrorBrand = Symbol.for('RapidoHttpClientErrorBrand');\n\n// eslint-disable-next-line @typescript-eslint/no-namespace\nexport namespace errors {\n export import UndiciError = undiciErrors.UndiciError;\n export import ConnectTimeoutError = undiciErrors.ConnectTimeoutError;\n export import HeadersTimeoutError = undiciErrors.HeadersTimeoutError;\n export import HeadersOverflowError = undiciErrors.HeadersOverflowError;\n export import BodyTimeoutError = undiciErrors.BodyTimeoutError;\n export import ResponseError = undiciErrors.ResponseError;\n export import InvalidArgumentError = undiciErrors.InvalidArgumentError;\n export import InvalidReturnValueError = undiciErrors.InvalidReturnValueError;\n export import RequestAbortedError = undiciErrors.RequestAbortedError;\n export import InformationalError = undiciErrors.InformationalError;\n export import RequestContentLengthMismatchError = undiciErrors.RequestContentLengthMismatchError;\n export import ResponseContentLengthMismatchError = undiciErrors.ResponseContentLengthMismatchError;\n export import ClientDestroyedError = undiciErrors.ClientDestroyedError;\n export import ClientClosedError = undiciErrors.ClientClosedError;\n export import SocketError = undiciErrors.SocketError;\n export import NotSupportedError = undiciErrors.NotSupportedError;\n export import BalancedPoolMissingUpstreamError = undiciErrors.BalancedPoolMissingUpstreamError;\n export import HTTPParserError = undiciErrors.HTTPParserError;\n export import ResponseExceededMaxSizeError = undiciErrors.ResponseExceededMaxSizeError;\n export import RequestRetryError = undiciErrors.RequestRetryError;\n export import SecureProxyConnectionError = undiciErrors.SecureProxyConnectionError;\n export import MaxOriginsReachedError = undiciErrors.MaxOriginsReachedError;\n\n export function isRapidoHttpClientError(value: unknown): value is RapidoHttpClientError<unknown> {\n return typeof value === 'object' && value !== null && RapidoHttpClientErrorBrand in value;\n }\n\n export class RapidoHttpClientError<T> extends undiciErrors.ResponseError {\n public readonly status: number;\n public readonly statusText: string;\n\n constructor(\n body: ConstructorParameters<typeof undiciErrors.ResponseError>[2]['body'],\n public readonly response: Dispatcher.ResponseData,\n public readonly data: T,\n ) {\n super(`HTTP Error ${response.statusCode}`, response.statusCode, { headers: response.headers, body });\n this.status = response.statusCode;\n this.statusText = STATUS_CODES[response.statusCode] || 'Unknown Status';\n\n Object.defineProperty(this, RapidoHttpClientErrorBrand, {\n value: true,\n enumerable: false,\n writable: false,\n });\n }\n\n public static isRapidoHttpClientError(value: unknown): value is RapidoHttpClientError<unknown> {\n return isRapidoHttpClientError(value);\n }\n }\n}\n","export const testAgentParam: unique symbol = Symbol('testAgentParam');\n","import type { Dispatcher } from 'undici';\nimport { getCurrentContext } from '@autofleet/outbreak';\n\nexport const outbreakInterceptor: Dispatcher.DispatcherComposeInterceptor = dispatch => function outbreakInterceptorInner(opts, handler) {\n const currentTraceContext = getCurrentContext().context;\n if (currentTraceContext) {\n // Convert headers to object format if they're in array format\n let headers: Record<string, string | string[]>;\n if (Array.isArray(opts.headers)) {\n /* c8 ignore next */\n headers = Object.fromEntries(opts.headers as unknown as [string, string | string[]][]);\n } else {\n headers = { ...opts.headers as Record<string, string | string[]> };\n }\n\n // Inject outbreak trace headers\n for (const [header, value] of currentTraceContext) {\n if (typeof value === 'symbol' || typeof value === 'undefined') {\n continue;\n }\n headers[header] = value as string;\n }\n opts.headers = headers;\n }\n return dispatch(opts, handler);\n};\n","import { STATUS_CODES } from 'node:http';\nimport type { ZodType, output } from 'zod';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\nimport type CacheHandler from 'undici/types/cache-interceptor';\nimport Undici, { Agent, type Dispatcher, interceptors } from 'undici';\nimport { errors } from './errors';\nimport { testAgentParam } from './testingHelper';\nimport { outbreakInterceptor } from './outbreakInterceptor';\nimport {\n createRequestString,\n defaultRetryMethod,\n defaultSettings,\n defaultSettingsWithDecompress,\n isFormData,\n mergeConfig,\n parseUrlAndOrigin,\n resolveServiceUrl,\n} from './utils';\n\nconst { cacheStores } = Undici;\nconst { cache, retry, decompress } = interceptors;\n\nexport interface RapidoHttpClientSettings {\n [testAgentParam]?: Dispatcher;\n /** The MS name to be used to resolve the base URL using K8S service discovery. Ignored if `serviceUrl` is provided as well. */\n serviceName?: string;\n /** The base URL to be used. Overrides `serviceName` when provided. */\n serviceUrl?: string;\n logger: LoggerInstanceManager;\n /** The request timeout in milliseconds. @default 10_000 */\n timeout?: number;\n /** Settings for retrying requests on failure */\n retries?: interceptors.RetryInterceptorOpts;\n /** Common headers applied to all requests of this `RapidoHttpClient` instance. */\n headers?: Record<string, string> | string[][];\n keepAliveTimeout?: number;\n keepAliveMaxTimeout?: number;\n /** The amount of concurrent connections to be maintained using a connection pool. @default 10 */\n connections?: number;\n /** The settings for caching requests */\n cache?: CacheHandler.MemoryCacheStoreOpts;\n /** Whether to automatically decompress response bodies. When `true` will automatically add an `Accept-Encoding` header. @default false */\n autoDecompress?: boolean;\n /**\n * A function to validate the HTTP response status code.\n * Return `true` to consider the status valid, `false` otherwise.\n *\n * When status is invalid, a {@link errors.RapidoHttpClientError `RapidoHttpClientError`} will be thrown.\n *\n * @example (status) => status >= 200 && status < 300\n * @example (status) => status !== 401\n *\n * @default (status) => status < 400\n */\n validateStatus?(status: number): boolean;\n}\n\nexport interface RequestConfig {\n params?: Record<string, any>;\n body?: any;\n /** The request timeout in milliseconds. This overrides the client's instance setting @default 10_000 */\n timeout?: number;\n /** Headers applied to this request, appending on the client's common headers. */\n headers?: Record<string, string>;\n /** An `AbortSignal` which should abort the request and it's parsing when aborted. */\n signal?: AbortSignal;\n /** A Zod schema defining what the response should look like. This also parses error responses. */\n responseSchema?: ZodType;\n /**\n * A function to validate the HTTP response status code.\n * Return `true` to consider the status valid, `false` otherwise.\n *\n * When status is invalid, a {@link errors.RapidoHttpClientError `RapidoHttpClientError`} will be thrown.\n *\n * @example (status) => status >= 200 && status < 300\n * @example (status) => status !== 401\n *\n * @default (status) => status < 400\n */\n validateStatus?(status: number): boolean;\n}\n\nexport interface RapidoHttpClientResponse<T = any> {\n data: T;\n status: number;\n statusText: string;\n headers: Record<string, string | string[]>;\n config: RequestConfig & { url: string; method: string; baseURL?: string; };\n}\n\nconst retriableMethods: Dispatcher.HttpMethod[] = ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE', 'QUERY'];\n\nexport class RapidoHttpClient {\n readonly #logger: LoggerInstanceManager;\n private readonly settings: RapidoHttpClientSettings & { baseURL?: string; basePath?: string; };\n public readonly client: Dispatcher.ComposedDispatcher;\n\n public get<T = unknown>(url: string, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public get<T extends ZodType>(url: string, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public get<T = unknown>(url: string, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('GET', url, undefined, config);\n }\n\n public post<T = unknown>(url: string, body?: any, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public post<T extends ZodType>(url: string, body: any, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public post<T = unknown>(url: string, body?: any, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('POST', url, body, config);\n }\n\n public delete<T = unknown>(url: string, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public delete<T extends ZodType>(url: string, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public delete<T = unknown>(url: string, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('DELETE', url, undefined, config);\n }\n\n public head<T = unknown>(url: string, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public head<T extends ZodType>(url: string, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public head<T = unknown>(url: string, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('HEAD', url, undefined, config);\n }\n\n public put<T = unknown>(url: string, body?: any, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public put<T extends ZodType>(url: string, body: any, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public put<T = unknown>(url: string, body?: any, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('PUT', url, body, config);\n }\n\n public patch<T = unknown>(url: string, body?: any, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public patch<T extends ZodType>(url: string, body: any, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public patch<T = unknown>(url: string, body?: any, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('PATCH', url, body, config);\n }\n\n public options<T = unknown>(url: string, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public options<T extends ZodType>(url: string, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public options<T = unknown>(url: string, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('OPTIONS', url, undefined, config);\n }\n\n public query<T = unknown>(url: string, body?: any, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public query<T extends ZodType>(url: string, body: any, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public query<T = unknown>(url: string, body?: any, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('QUERY', url, body, config);\n }\n\n constructor(settings: RapidoHttpClientSettings) {\n this.#logger = settings.logger;\n this.settings = mergeConfig<typeof defaultSettings, Partial<typeof settings>>(\n settings.autoDecompress ? defaultSettingsWithDecompress : defaultSettings,\n settings,\n ) as RapidoHttpClientSettings & { baseURL?: string; };\n this.#createBaseUrl();\n\n // In test mode, use the test dispatcher directly (MockAgent or MockPool) Otherwise, create an Agent which creates Client Pools\n // Check for global mock agent first (for testing), then test param, then create real Agent\n /* c8 ignore next */\n const globalMockAgent = (globalThis as any).__RAPIDO_HTTP_CLIENT_MOCK_AGENT__;\n /* c8 ignore next */\n this.client = this.settings[testAgentParam] ?? globalMockAgent ?? new Agent({\n connect: {\n timeout: this.settings.timeout,\n },\n keepAliveTimeout: this.settings.keepAliveTimeout,\n keepAliveMaxTimeout: this.settings.keepAliveMaxTimeout,\n connections: this.settings.connections,\n });\n\n if (this.settings.cache) {\n this.client = this.client.compose(cache({\n store: new cacheStores.MemoryCacheStore({\n ...this.settings.cache,\n }),\n }));\n }\n\n if (this.settings.retries) {\n this.client = this.client.compose(retry({\n methods: retriableMethods,\n ...this.settings.retries,\n retry: (err, context, callback) => {\n const { counter } = context.state;\n /* c8 ignore next */\n const maxRetries = context.opts.retryOptions?.maxRetries ?? 5;\n\n const retryToUse = this.settings.retries?.retry ?? defaultRetryMethod;\n const callbackWithLogging = (result?: Error | null) => {\n if (!result) {\n const { method, origin, path } = context.opts;\n const baseUrl = (!origin || typeof origin === 'string') ? origin : origin.origin;\n this.#logger.warn(`Request retry attempt ${counter}/${maxRetries}: ${createRequestString(method, baseUrl, path)}`, { err });\n }\n callback(result);\n };\n retryToUse(err, context, callbackWithLogging);\n },\n }));\n }\n\n if (this.settings.autoDecompress) {\n this.client = this.client.compose(decompress({ skipErrorResponses: false }));\n }\n\n this.client = this.client.compose(outbreakInterceptor);\n }\n\n #createBaseUrl(): void {\n const fullUrl = resolveServiceUrl(this.settings);\n const urlObj = new URL(fullUrl);\n this.settings.baseURL = urlObj.origin;\n this.settings.basePath = urlObj.pathname !== '/' ? urlObj.pathname : '';\n }\n\n #getSignal(currentSignal?: AbortSignal, timeout?: number): { signal?: AbortSignal; timeoutSignal?: AbortSignal; } {\n if (!currentSignal && !timeout) {\n return { signal: undefined, timeoutSignal: undefined };\n }\n\n if (currentSignal && !timeout) {\n return { signal: currentSignal, timeoutSignal: undefined };\n }\n\n const timeoutSignal = AbortSignal.timeout(timeout!);\n if (!currentSignal && timeout) {\n return { signal: timeoutSignal, timeoutSignal };\n }\n\n return { signal: AbortSignal.any([currentSignal!, timeoutSignal]), timeoutSignal };\n }\n\n #prepareRequestBody(body: any, headers: Record<string, string>): string | Buffer | FormData | undefined {\n if (body === undefined || body === null) {\n return undefined;\n }\n\n // Handle FormData instances automatically\n if (isFormData(body)) {\n if (!headers['Content-Type'] && !headers['content-type']) {\n headers['Content-Type'] = 'application/x-www-form-urlencoded';\n }\n return body;\n }\n\n if (typeof body === 'object' && !Buffer.isBuffer(body)) {\n // Check if Content-Type is set to application/x-www-form-urlencoded\n const contentType = headers['Content-Type'] || headers['content-type'];\n if (contentType && /application\\/x-www-form-urlencoded/i.test(contentType)) {\n // Encode as URL parameters\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(body)) {\n if (Array.isArray(value)) {\n value.forEach(v => params.append(key, String(v)));\n } else if (value !== undefined && value !== null) {\n // eslint-disable-next-line @typescript-eslint/no-base-to-string\n params.append(key, String(value));\n }\n }\n return params.toString();\n }\n // Default to JSON\n if (!headers['Content-Type'] && !headers['content-type']) {\n headers['Content-Type'] = 'application/json';\n }\n return JSON.stringify(body);\n } else if (typeof body === 'string') {\n return body;\n } else if (Buffer.isBuffer(body)) {\n return body;\n }\n return String(body);\n }\n\n async #request<T = any>(\n method: Dispatcher.HttpMethod,\n url: string,\n body?: any,\n config: RequestConfig = {},\n ): Promise<RapidoHttpClientResponse<T>> {\n const mergedConfig = mergeConfig(this.settings, config);\n\n if (mergedConfig.signal?.aborted) {\n this.#logger.error(`Request aborted before start: ${createRequestString(method, mergedConfig.baseURL, url)}`);\n throw new errors.RequestAbortedError(mergedConfig.signal.reason?.message || 'Request aborted', { cause: mergedConfig.signal.reason });\n }\n\n const headers: Record<string, string> = { ...mergedConfig.headers };\n const requestBody = this.#prepareRequestBody(body, headers);\n\n const { origin, path } = parseUrlAndOrigin(url, mergedConfig.baseURL!, mergedConfig.basePath);\n const requestInfo = { method, baseURL: origin, url };\n\n if (mergedConfig.params) {\n for (const [key, value] of Object.entries(mergedConfig.params)) {\n if (Array.isArray(value) && !key.endsWith('[]')) {\n mergedConfig.params[`${key}[]`] = value;\n delete mergedConfig.params[key];\n }\n }\n }\n\n const { signal, timeoutSignal } = this.#getSignal(mergedConfig.signal, mergedConfig.timeout);\n\n try {\n this.#logger.info(`Start Request: ${createRequestString(method, origin, path)}`);\n\n const requestOptions: Dispatcher.RequestOptions = {\n path,\n origin,\n method,\n signal,\n headers,\n body: requestBody,\n query: mergedConfig.params,\n };\n\n const response = await this.client.request(requestOptions);\n\n const isStatusValid = mergedConfig.validateStatus ?? (() => response.statusCode < 400);\n if (!isStatusValid(response.statusCode)) {\n const data = await this.#parseResponse<T>(response, config.responseSchema);\n this.#logger.error(`Finish Request with error ${createRequestString(method, origin, path)}`, {\n status: response.statusCode,\n data,\n });\n\n throw new errors.RapidoHttpClientError<T>(requestBody, response, data);\n }\n\n this.#logger.info(`Finish Request: ${createRequestString(method, origin, path)}`);\n\n const data = response.statusCode !== 204 ? await this.#parseResponse<T>(response, config.responseSchema) : undefined as T;\n\n return {\n data,\n status: response.statusCode,\n statusText: STATUS_CODES[response.statusCode] || '',\n headers: response.headers as Record<string, string | string[]>,\n config: requestInfo,\n };\n } catch (error) {\n if (timeoutSignal && timeoutSignal.reason === error) {\n // If the timeoutSignal aborted (config timeout), throw a timeout-specific error\n this.#logger.error(`Request timeout: ${createRequestString(method, origin, path)}`);\n const error = new errors.UndiciError(`Request timeout after ${mergedConfig.timeout}ms`, { cause: timeoutSignal.reason });\n error.code = 'UND_ERR_TIMEOUT';\n throw error;\n }\n if (signal && signal.reason === error) {\n // If the user's signal aborted, throw RequestAbortedError\n this.#logger.error(`Request aborted: ${createRequestString(method, origin, path)}`);\n throw new errors.RequestAbortedError(signal.reason.message, { cause: signal.reason });\n }\n\n this.#logger.error(`Finish Request with error ${createRequestString(method, origin, path)}`, { error });\n throw error;\n }\n }\n\n async #parseResponse<T>(response: Dispatcher.ResponseData, schema?: ZodType): Promise<T> {\n const contentType = (response.headers['content-type'] as string) || '';\n const bodyText = await response.body.text();\n\n if (!bodyText) {\n return undefined as T;\n }\n\n if (/text/i.test(contentType)) {\n return bodyText as T;\n }\n\n let json: unknown;\n try {\n json = JSON.parse(bodyText);\n } catch {\n return bodyText as T;\n }\n return (schema ? schema.parse(json) : json) as T;\n }\n\n /**\n * @param url The endpoint URL to send the request to.\n * @param options Additional options to include in the request payload.\n * @returns A promise that resolves to an array of all results.\n */\n async getAllPages<T>(url: string, options: RequestConfig = {}): Promise<T[]> {\n const currentResult: T[] = [];\n let resultsInFirstPage: number | null = null;\n let lastResultsSize: number | null = null;\n\n const localOptions = { ...options, params: { ...options.params, page: 1 } };\n // Make sure even if `options` had `params` we initialize `page` to 1.\n localOptions.params.page ||= 1;\n\n while ((localOptions.params.page === 1 || lastResultsSize === resultsInFirstPage) && lastResultsSize !== 0) {\n const { data } = await this.get<T[]>(url, localOptions as Omit<RequestConfig, 'responseSchema'>);\n lastResultsSize = data.length;\n if (localOptions.params.page === 1) {\n resultsInFirstPage = data.length;\n }\n\n localOptions.params.page += 1;\n Array.prototype.push.apply(currentResult, data);\n }\n\n return currentResult;\n }\n\n /**\n * Fetches all pages from a paginated API endpoint until all results are retrieved\n * or an optional page limit is reached.\n *\n * @param url The endpoint URL to send the request to.\n * @param options Additional options to include in the request payload.\n * @param pageLimit The maximum number of pages to fetch. Set to -1 for no limit.\n * @returns A promise that resolves to an array of all results.\n */\n async getAllPagesFromQueryEndpoint<T>(url: string, options: object = {}, pageLimit = 100): Promise<T[]> {\n const currentResult: T[] = [];\n\n let moreResultsToLoad = true;\n let page = 1;\n while (moreResultsToLoad && (pageLimit === -1 || page < pageLimit)) {\n const { data } = await this.post<{ rows: T[]; count: number; }>(url, {\n ...options,\n page,\n });\n const { rows, count } = data;\n page += 1;\n Array.prototype.push.apply(currentResult, rows);\n moreResultsToLoad = currentResult.length < count;\n }\n\n return currentResult;\n }\n\n async close(): Promise<void> {\n await this.client.close();\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.close();\n }\n}\n\nexport default RapidoHttpClient;\nexport { errors };\n"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -32,6 +32,7 @@ declare namespace errors {
|
|
|
32
32
|
readonly response: Dispatcher.ResponseData;
|
|
33
33
|
readonly data: T;
|
|
34
34
|
readonly status: number;
|
|
35
|
+
readonly statusText: string;
|
|
35
36
|
constructor(body: ConstructorParameters<typeof errors$1.ResponseError>[2]["body"], response: Dispatcher.ResponseData, data: T);
|
|
36
37
|
static isRapidoHttpClientError(value: unknown): value is RapidoHttpClientError<unknown>;
|
|
37
38
|
}
|
|
@@ -43,25 +44,61 @@ declare const testAgentParam: unique symbol;
|
|
|
43
44
|
//#region src/index.d.ts
|
|
44
45
|
interface RapidoHttpClientSettings {
|
|
45
46
|
[testAgentParam]?: Dispatcher;
|
|
47
|
+
/** The MS name to be used to resolve the base URL using K8S service discovery. Ignored if `serviceUrl` is provided as well. */
|
|
46
48
|
serviceName?: string;
|
|
49
|
+
/** The base URL to be used. Overrides `serviceName` when provided. */
|
|
47
50
|
serviceUrl?: string;
|
|
48
51
|
logger: LoggerInstanceManager;
|
|
52
|
+
/** The request timeout in milliseconds. @default 10_000 */
|
|
49
53
|
timeout?: number;
|
|
54
|
+
/** Settings for retrying requests on failure */
|
|
50
55
|
retries?: interceptors.RetryInterceptorOpts;
|
|
56
|
+
/** Common headers applied to all requests of this `RapidoHttpClient` instance. */
|
|
51
57
|
headers?: Record<string, string> | string[][];
|
|
52
58
|
keepAliveTimeout?: number;
|
|
53
59
|
keepAliveMaxTimeout?: number;
|
|
60
|
+
/** The amount of concurrent connections to be maintained using a connection pool. @default 10 */
|
|
54
61
|
connections?: number;
|
|
62
|
+
/** The settings for caching requests */
|
|
55
63
|
cache?: CacheHandler.MemoryCacheStoreOpts;
|
|
64
|
+
/** Whether to automatically decompress response bodies. When `true` will automatically add an `Accept-Encoding` header. @default false */
|
|
56
65
|
autoDecompress?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* A function to validate the HTTP response status code.
|
|
68
|
+
* Return `true` to consider the status valid, `false` otherwise.
|
|
69
|
+
*
|
|
70
|
+
* When status is invalid, a {@link errors.RapidoHttpClientError `RapidoHttpClientError`} will be thrown.
|
|
71
|
+
*
|
|
72
|
+
* @example (status) => status >= 200 && status < 300
|
|
73
|
+
* @example (status) => status !== 401
|
|
74
|
+
*
|
|
75
|
+
* @default (status) => status < 400
|
|
76
|
+
*/
|
|
77
|
+
validateStatus?(status: number): boolean;
|
|
57
78
|
}
|
|
58
79
|
interface RequestConfig {
|
|
59
80
|
params?: Record<string, any>;
|
|
60
81
|
body?: any;
|
|
82
|
+
/** The request timeout in milliseconds. This overrides the client's instance setting @default 10_000 */
|
|
61
83
|
timeout?: number;
|
|
84
|
+
/** Headers applied to this request, appending on the client's common headers. */
|
|
62
85
|
headers?: Record<string, string>;
|
|
86
|
+
/** An `AbortSignal` which should abort the request and it's parsing when aborted. */
|
|
63
87
|
signal?: AbortSignal;
|
|
88
|
+
/** A Zod schema defining what the response should look like. This also parses error responses. */
|
|
64
89
|
responseSchema?: ZodType;
|
|
90
|
+
/**
|
|
91
|
+
* A function to validate the HTTP response status code.
|
|
92
|
+
* Return `true` to consider the status valid, `false` otherwise.
|
|
93
|
+
*
|
|
94
|
+
* When status is invalid, a {@link errors.RapidoHttpClientError `RapidoHttpClientError`} will be thrown.
|
|
95
|
+
*
|
|
96
|
+
* @example (status) => status >= 200 && status < 300
|
|
97
|
+
* @example (status) => status !== 401
|
|
98
|
+
*
|
|
99
|
+
* @default (status) => status < 400
|
|
100
|
+
*/
|
|
101
|
+
validateStatus?(status: number): boolean;
|
|
65
102
|
}
|
|
66
103
|
interface RapidoHttpClientResponse<T = any> {
|
|
67
104
|
data: T;
|
package/dist/index.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ declare namespace errors {
|
|
|
32
32
|
readonly response: Dispatcher.ResponseData;
|
|
33
33
|
readonly data: T;
|
|
34
34
|
readonly status: number;
|
|
35
|
+
readonly statusText: string;
|
|
35
36
|
constructor(body: ConstructorParameters<typeof errors$1.ResponseError>[2]["body"], response: Dispatcher.ResponseData, data: T);
|
|
36
37
|
static isRapidoHttpClientError(value: unknown): value is RapidoHttpClientError<unknown>;
|
|
37
38
|
}
|
|
@@ -43,25 +44,61 @@ declare const testAgentParam: unique symbol;
|
|
|
43
44
|
//#region src/index.d.ts
|
|
44
45
|
interface RapidoHttpClientSettings {
|
|
45
46
|
[testAgentParam]?: Dispatcher;
|
|
47
|
+
/** The MS name to be used to resolve the base URL using K8S service discovery. Ignored if `serviceUrl` is provided as well. */
|
|
46
48
|
serviceName?: string;
|
|
49
|
+
/** The base URL to be used. Overrides `serviceName` when provided. */
|
|
47
50
|
serviceUrl?: string;
|
|
48
51
|
logger: LoggerInstanceManager;
|
|
52
|
+
/** The request timeout in milliseconds. @default 10_000 */
|
|
49
53
|
timeout?: number;
|
|
54
|
+
/** Settings for retrying requests on failure */
|
|
50
55
|
retries?: interceptors.RetryInterceptorOpts;
|
|
56
|
+
/** Common headers applied to all requests of this `RapidoHttpClient` instance. */
|
|
51
57
|
headers?: Record<string, string> | string[][];
|
|
52
58
|
keepAliveTimeout?: number;
|
|
53
59
|
keepAliveMaxTimeout?: number;
|
|
60
|
+
/** The amount of concurrent connections to be maintained using a connection pool. @default 10 */
|
|
54
61
|
connections?: number;
|
|
62
|
+
/** The settings for caching requests */
|
|
55
63
|
cache?: CacheHandler.MemoryCacheStoreOpts;
|
|
64
|
+
/** Whether to automatically decompress response bodies. When `true` will automatically add an `Accept-Encoding` header. @default false */
|
|
56
65
|
autoDecompress?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* A function to validate the HTTP response status code.
|
|
68
|
+
* Return `true` to consider the status valid, `false` otherwise.
|
|
69
|
+
*
|
|
70
|
+
* When status is invalid, a {@link errors.RapidoHttpClientError `RapidoHttpClientError`} will be thrown.
|
|
71
|
+
*
|
|
72
|
+
* @example (status) => status >= 200 && status < 300
|
|
73
|
+
* @example (status) => status !== 401
|
|
74
|
+
*
|
|
75
|
+
* @default (status) => status < 400
|
|
76
|
+
*/
|
|
77
|
+
validateStatus?(status: number): boolean;
|
|
57
78
|
}
|
|
58
79
|
interface RequestConfig {
|
|
59
80
|
params?: Record<string, any>;
|
|
60
81
|
body?: any;
|
|
82
|
+
/** The request timeout in milliseconds. This overrides the client's instance setting @default 10_000 */
|
|
61
83
|
timeout?: number;
|
|
84
|
+
/** Headers applied to this request, appending on the client's common headers. */
|
|
62
85
|
headers?: Record<string, string>;
|
|
86
|
+
/** An `AbortSignal` which should abort the request and it's parsing when aborted. */
|
|
63
87
|
signal?: AbortSignal;
|
|
88
|
+
/** A Zod schema defining what the response should look like. This also parses error responses. */
|
|
64
89
|
responseSchema?: ZodType;
|
|
90
|
+
/**
|
|
91
|
+
* A function to validate the HTTP response status code.
|
|
92
|
+
* Return `true` to consider the status valid, `false` otherwise.
|
|
93
|
+
*
|
|
94
|
+
* When status is invalid, a {@link errors.RapidoHttpClientError `RapidoHttpClientError`} will be thrown.
|
|
95
|
+
*
|
|
96
|
+
* @example (status) => status >= 200 && status < 300
|
|
97
|
+
* @example (status) => status !== 401
|
|
98
|
+
*
|
|
99
|
+
* @default (status) => status < 400
|
|
100
|
+
*/
|
|
101
|
+
validateStatus?(status: number): boolean;
|
|
65
102
|
}
|
|
66
103
|
interface RapidoHttpClientResponse<T = any> {
|
|
67
104
|
data: T;
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a as e,c as t,i as n,n as r,o as i,r as a,s as o,t as s}from"./utils-
|
|
1
|
+
import{a as e,c as t,i as n,n as r,o as i,r as a,s as o,t as s}from"./utils-CN26OYva.js";import{STATUS_CODES as c}from"node:http";import l,{Agent as u,errors as d,interceptors as f}from"undici";import{getCurrentContext as p}from"@autofleet/outbreak";const m=Symbol.for(`RapidoHttpClientErrorBrand`);let h;(function(e){e.UndiciError=d.UndiciError,e.ConnectTimeoutError=d.ConnectTimeoutError,e.HeadersTimeoutError=d.HeadersTimeoutError,e.HeadersOverflowError=d.HeadersOverflowError,e.BodyTimeoutError=d.BodyTimeoutError,e.ResponseError=d.ResponseError,e.InvalidArgumentError=d.InvalidArgumentError,e.InvalidReturnValueError=d.InvalidReturnValueError,e.RequestAbortedError=d.RequestAbortedError,e.InformationalError=d.InformationalError,e.RequestContentLengthMismatchError=d.RequestContentLengthMismatchError,e.ResponseContentLengthMismatchError=d.ResponseContentLengthMismatchError,e.ClientDestroyedError=d.ClientDestroyedError,e.ClientClosedError=d.ClientClosedError,e.SocketError=d.SocketError,e.NotSupportedError=d.NotSupportedError,e.BalancedPoolMissingUpstreamError=d.BalancedPoolMissingUpstreamError,e.HTTPParserError=d.HTTPParserError,e.ResponseExceededMaxSizeError=d.ResponseExceededMaxSizeError,e.RequestRetryError=d.RequestRetryError,e.SecureProxyConnectionError=d.SecureProxyConnectionError,e.MaxOriginsReachedError=d.MaxOriginsReachedError;function t(e){return typeof e==`object`&&!!e&&m in e}e.isRapidoHttpClientError=t;class n extends d.ResponseError{constructor(e,t,n){super(`HTTP Error ${t.statusCode}`,t.statusCode,{headers:t.headers,body:e}),this.response=t,this.data=n,this.status=t.statusCode,this.statusText=c[t.statusCode]||`Unknown Status`,Object.defineProperty(this,m,{value:!0,enumerable:!1,writable:!1})}static isRapidoHttpClientError(e){return t(e)}}e.RapidoHttpClientError=n})(h||={});const g=Symbol(`testAgentParam`),_=e=>function(t,n){let r=p().context;if(r){let e;e=Array.isArray(t.headers)?Object.fromEntries(t.headers):{...t.headers};for(let[t,n]of r)typeof n==`symbol`||n===void 0||(e[t]=n);t.headers=e}return e(t,n)},{cacheStores:v}=l,{cache:y,retry:b,decompress:x}=f,S=[`GET`,`HEAD`,`OPTIONS`,`PUT`,`DELETE`,`TRACE`,`QUERY`];var C=class{#e;get(e,t){return this.#i(`GET`,e,void 0,t)}post(e,t,n){return this.#i(`POST`,e,t,n)}delete(e,t){return this.#i(`DELETE`,e,void 0,t)}head(e,t){return this.#i(`HEAD`,e,void 0,t)}put(e,t,n){return this.#i(`PUT`,e,t,n)}patch(e,t,n){return this.#i(`PATCH`,e,t,n)}options(e,t){return this.#i(`OPTIONS`,e,void 0,t)}query(e,t,n){return this.#i(`QUERY`,e,t,n)}constructor(e){this.#e=e.logger,this.settings=i(e.autoDecompress?n:a,e),this.#t();let t=globalThis.__RAPIDO_HTTP_CLIENT_MOCK_AGENT__;this.client=this.settings[g]??t??new u({connect:{timeout:this.settings.timeout},keepAliveTimeout:this.settings.keepAliveTimeout,keepAliveMaxTimeout:this.settings.keepAliveMaxTimeout,connections:this.settings.connections}),this.settings.cache&&(this.client=this.client.compose(y({store:new v.MemoryCacheStore({...this.settings.cache})}))),this.settings.retries&&(this.client=this.client.compose(b({methods:S,...this.settings.retries,retry:(e,t,n)=>{let{counter:i}=t.state,a=t.opts.retryOptions?.maxRetries??5;(this.settings.retries?.retry??r)(e,t,r=>{if(!r){let{method:n,origin:r,path:o}=t.opts,c=!r||typeof r==`string`?r:r.origin;this.#e.warn(`Request retry attempt ${i}/${a}: ${s(n,c,o)}`,{err:e})}n(r)})}}))),this.settings.autoDecompress&&(this.client=this.client.compose(x({skipErrorResponses:!1}))),this.client=this.client.compose(_)}#t(){let e=t(this.settings),n=new URL(e);this.settings.baseURL=n.origin,this.settings.basePath=n.pathname===`/`?``:n.pathname}#n(e,t){if(!e&&!t)return{signal:void 0,timeoutSignal:void 0};if(e&&!t)return{signal:e,timeoutSignal:void 0};let n=AbortSignal.timeout(t);return!e&&t?{signal:n,timeoutSignal:n}:{signal:AbortSignal.any([e,n]),timeoutSignal:n}}#r(t,n){if(t!=null){if(e(t))return!n[`Content-Type`]&&!n[`content-type`]&&(n[`Content-Type`]=`application/x-www-form-urlencoded`),t;if(typeof t==`object`&&!Buffer.isBuffer(t)){let e=n[`Content-Type`]||n[`content-type`];if(e&&/application\/x-www-form-urlencoded/i.test(e)){let e=new URLSearchParams;for(let[n,r]of Object.entries(t))Array.isArray(r)?r.forEach(t=>e.append(n,String(t))):r!=null&&e.append(n,String(r));return e.toString()}return!n[`Content-Type`]&&!n[`content-type`]&&(n[`Content-Type`]=`application/json`),JSON.stringify(t)}else if(typeof t==`string`)return t;else if(Buffer.isBuffer(t))return t;return String(t)}}async#i(e,t,n,r={}){let a=i(this.settings,r);if(a.signal?.aborted)throw this.#e.error(`Request aborted before start: ${s(e,a.baseURL,t)}`),new h.RequestAbortedError(a.signal.reason?.message||`Request aborted`,{cause:a.signal.reason});let l={...a.headers},u=this.#r(n,l),{origin:d,path:f}=o(t,a.baseURL,a.basePath),p={method:e,baseURL:d,url:t};if(a.params)for(let[e,t]of Object.entries(a.params))Array.isArray(t)&&!e.endsWith(`[]`)&&(a.params[`${e}[]`]=t,delete a.params[e]);let{signal:m,timeoutSignal:g}=this.#n(a.signal,a.timeout);try{this.#e.info(`Start Request: ${s(e,d,f)}`);let t={path:f,origin:d,method:e,signal:m,headers:l,body:u,query:a.params},n=await this.client.request(t);if(!(a.validateStatus??(()=>n.statusCode<400))(n.statusCode)){let t=await this.#a(n,r.responseSchema);throw this.#e.error(`Finish Request with error ${s(e,d,f)}`,{status:n.statusCode,data:t}),new h.RapidoHttpClientError(u,n,t)}return this.#e.info(`Finish Request: ${s(e,d,f)}`),{data:n.statusCode===204?void 0:await this.#a(n,r.responseSchema),status:n.statusCode,statusText:c[n.statusCode]||``,headers:n.headers,config:p}}catch(t){if(g&&g.reason===t){this.#e.error(`Request timeout: ${s(e,d,f)}`);let t=new h.UndiciError(`Request timeout after ${a.timeout}ms`,{cause:g.reason});throw t.code=`UND_ERR_TIMEOUT`,t}throw m&&m.reason===t?(this.#e.error(`Request aborted: ${s(e,d,f)}`),new h.RequestAbortedError(m.reason.message,{cause:m.reason})):(this.#e.error(`Finish Request with error ${s(e,d,f)}`,{error:t}),t)}}async#a(e,t){let n=e.headers[`content-type`]||``,r=await e.body.text();if(!r)return;if(/text/i.test(n))return r;let i;try{i=JSON.parse(r)}catch{return r}return t?t.parse(i):i}async getAllPages(e,t={}){let n=[],r=null,i=null,a={...t,params:{...t.params,page:1}};for(a.params.page||=1;(a.params.page===1||i===r)&&i!==0;){let{data:t}=await this.get(e,a);i=t.length,a.params.page===1&&(r=t.length),a.params.page+=1,Array.prototype.push.apply(n,t)}return n}async getAllPagesFromQueryEndpoint(e,t={},n=100){let r=[],i=!0,a=1;for(;i&&(n===-1||a<n);){let{data:n}=await this.post(e,{...t,page:a}),{rows:o,count:s}=n;a+=1,Array.prototype.push.apply(r,o),i=r.length<s}return r}async close(){await this.client.close()}async[Symbol.asyncDispose](){await this.close()}},w=C;export{C as RapidoHttpClient,w as default,h as errors};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["undiciErrors","response: Dispatcher.ResponseData","data: T","testAgentParam: unique symbol","outbreakInterceptor: Dispatcher.DispatcherComposeInterceptor","headers: Record<string, string | string[]>","retriableMethods: Dispatcher.HttpMethod[]","#logger","#request","#createBaseUrl","headers: Record<string, string>","#prepareRequestBody","#getSignal","requestOptions: Dispatcher.RequestOptions","#parseResponse","error","json: unknown","currentResult: T[]","resultsInFirstPage: number | null","lastResultsSize: number | null"],"sources":["../src/errors.ts","../src/testingHelper.ts","../src/outbreakInterceptor.ts","../src/index.ts"],"sourcesContent":["import { type Dispatcher, errors as undiciErrors } from 'undici';\n\nconst RapidoHttpClientErrorBrand = Symbol.for('RapidoHttpClientErrorBrand');\n\n// eslint-disable-next-line @typescript-eslint/no-namespace\nexport namespace errors {\n export import UndiciError = undiciErrors.UndiciError;\n export import ConnectTimeoutError = undiciErrors.ConnectTimeoutError;\n export import HeadersTimeoutError = undiciErrors.HeadersTimeoutError;\n export import HeadersOverflowError = undiciErrors.HeadersOverflowError;\n export import BodyTimeoutError = undiciErrors.BodyTimeoutError;\n export import ResponseError = undiciErrors.ResponseError;\n export import InvalidArgumentError = undiciErrors.InvalidArgumentError;\n export import InvalidReturnValueError = undiciErrors.InvalidReturnValueError;\n export import RequestAbortedError = undiciErrors.RequestAbortedError;\n export import InformationalError = undiciErrors.InformationalError;\n export import RequestContentLengthMismatchError = undiciErrors.RequestContentLengthMismatchError;\n export import ResponseContentLengthMismatchError = undiciErrors.ResponseContentLengthMismatchError;\n export import ClientDestroyedError = undiciErrors.ClientDestroyedError;\n export import ClientClosedError = undiciErrors.ClientClosedError;\n export import SocketError = undiciErrors.SocketError;\n export import NotSupportedError = undiciErrors.NotSupportedError;\n export import BalancedPoolMissingUpstreamError = undiciErrors.BalancedPoolMissingUpstreamError;\n export import HTTPParserError = undiciErrors.HTTPParserError;\n export import ResponseExceededMaxSizeError = undiciErrors.ResponseExceededMaxSizeError;\n export import RequestRetryError = undiciErrors.RequestRetryError;\n export import SecureProxyConnectionError = undiciErrors.SecureProxyConnectionError;\n export import MaxOriginsReachedError = undiciErrors.MaxOriginsReachedError;\n\n export function isRapidoHttpClientError(value: unknown): value is RapidoHttpClientError<unknown> {\n return typeof value === 'object' && value !== null && RapidoHttpClientErrorBrand in value;\n }\n\n export class RapidoHttpClientError<T> extends undiciErrors.ResponseError {\n public readonly status: number;\n\n constructor(\n body: ConstructorParameters<typeof undiciErrors.ResponseError>[2]['body'],\n public readonly response: Dispatcher.ResponseData,\n public readonly data: T,\n ) {\n super(`HTTP Error ${response.statusCode}`, response.statusCode, { headers: response.headers, body });\n this.status = response.statusCode;\n\n Object.defineProperty(this, RapidoHttpClientErrorBrand, {\n value: true,\n enumerable: false,\n writable: false,\n });\n }\n\n public static isRapidoHttpClientError(value: unknown): value is RapidoHttpClientError<unknown> {\n return isRapidoHttpClientError(value);\n }\n }\n}\n","export const testAgentParam: unique symbol = Symbol('testAgentParam');\n","import type { Dispatcher } from 'undici';\nimport { getCurrentContext } from '@autofleet/outbreak';\n\nexport const outbreakInterceptor: Dispatcher.DispatcherComposeInterceptor = dispatch => function outbreakInterceptorInner(opts, handler) {\n const currentTraceContext = getCurrentContext().context;\n if (currentTraceContext) {\n // Convert headers to object format if they're in array format\n let headers: Record<string, string | string[]>;\n if (Array.isArray(opts.headers)) {\n /* c8 ignore next */\n headers = Object.fromEntries(opts.headers as unknown as [string, string | string[]][]);\n } else {\n headers = { ...opts.headers as Record<string, string | string[]> };\n }\n\n // Inject outbreak trace headers\n for (const [header, value] of currentTraceContext) {\n if (typeof value === 'symbol' || typeof value === 'undefined') {\n continue;\n }\n headers[header] = value as string;\n }\n opts.headers = headers;\n }\n return dispatch(opts, handler);\n};\n","import type { ZodType, output } from 'zod';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\nimport type CacheHandler from 'undici/types/cache-interceptor';\nimport Undici, { Agent, type Dispatcher, interceptors } from 'undici';\nimport { errors } from './errors';\nimport { testAgentParam } from './testingHelper';\nimport { outbreakInterceptor } from './outbreakInterceptor';\nimport {\n createRequestString,\n defaultRetryMethod,\n defaultSettings,\n defaultSettingsWithDecompress,\n isFormData,\n mergeConfig,\n parseUrlAndOrigin,\n resolveServiceUrl,\n} from './utils';\n\nconst { cacheStores } = Undici;\nconst { cache, retry, decompress } = interceptors;\n\nexport interface RapidoHttpClientSettings {\n [testAgentParam]?: Dispatcher;\n serviceName?: string;\n serviceUrl?: string;\n logger: LoggerInstanceManager;\n timeout?: number;\n retries?: interceptors.RetryInterceptorOpts;\n headers?: Record<string, string> | string[][];\n keepAliveTimeout?: number;\n keepAliveMaxTimeout?: number;\n connections?: number;\n cache?: CacheHandler.MemoryCacheStoreOpts;\n autoDecompress?: boolean;\n}\n\nexport interface RequestConfig {\n params?: Record<string, any>;\n body?: any;\n timeout?: number;\n headers?: Record<string, string>;\n signal?: AbortSignal;\n responseSchema?: ZodType;\n}\n\nexport interface RapidoHttpClientResponse<T = any> {\n data: T;\n status: number;\n statusText: string;\n headers: Record<string, string | string[]>;\n config: RequestConfig & { url: string; method: string; baseURL?: string; };\n}\n\nconst retriableMethods: Dispatcher.HttpMethod[] = ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE', 'QUERY'];\n\nexport class RapidoHttpClient {\n readonly #logger: LoggerInstanceManager;\n private readonly settings: RapidoHttpClientSettings & { baseURL?: string; basePath?: string; };\n public readonly client: Dispatcher.ComposedDispatcher;\n\n public get<T = unknown>(url: string, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public get<T extends ZodType>(url: string, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public get<T = unknown>(url: string, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('GET', url, undefined, config);\n }\n\n public post<T = unknown>(url: string, body?: any, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public post<T extends ZodType>(url: string, body: any, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public post<T = unknown>(url: string, body?: any, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('POST', url, body, config);\n }\n\n public delete<T = unknown>(url: string, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public delete<T extends ZodType>(url: string, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public delete<T = unknown>(url: string, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('DELETE', url, undefined, config);\n }\n\n public head<T = unknown>(url: string, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public head<T extends ZodType>(url: string, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public head<T = unknown>(url: string, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('HEAD', url, undefined, config);\n }\n\n public put<T = unknown>(url: string, body?: any, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public put<T extends ZodType>(url: string, body: any, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public put<T = unknown>(url: string, body?: any, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('PUT', url, body, config);\n }\n\n public patch<T = unknown>(url: string, body?: any, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public patch<T extends ZodType>(url: string, body: any, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public patch<T = unknown>(url: string, body?: any, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('PATCH', url, body, config);\n }\n\n public options<T = unknown>(url: string, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public options<T extends ZodType>(url: string, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public options<T = unknown>(url: string, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('OPTIONS', url, undefined, config);\n }\n\n public query<T = unknown>(url: string, body?: any, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public query<T extends ZodType>(url: string, body: any, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public query<T = unknown>(url: string, body?: any, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('QUERY', url, body, config);\n }\n\n constructor(settings: RapidoHttpClientSettings) {\n this.#logger = settings.logger;\n this.settings = mergeConfig<typeof defaultSettings, Partial<typeof settings>>(\n settings.autoDecompress ? defaultSettingsWithDecompress : defaultSettings,\n settings,\n ) as RapidoHttpClientSettings & { baseURL?: string; };\n this.#createBaseUrl();\n\n // In test mode, use the test dispatcher directly (MockAgent or MockPool) Otherwise, create an Agent which creates Client Pools\n // Check for global mock agent first (for testing), then test param, then create real Agent\n /* c8 ignore next */\n const globalMockAgent = (globalThis as any).__RAPIDO_HTTP_CLIENT_MOCK_AGENT__;\n /* c8 ignore next */\n this.client = this.settings[testAgentParam] ?? globalMockAgent ?? new Agent({\n connect: {\n timeout: this.settings.timeout,\n },\n keepAliveTimeout: this.settings.keepAliveTimeout,\n keepAliveMaxTimeout: this.settings.keepAliveMaxTimeout,\n connections: this.settings.connections,\n });\n\n if (this.settings.cache) {\n this.client = this.client.compose(cache({\n store: new cacheStores.MemoryCacheStore({\n ...this.settings.cache,\n }),\n }));\n }\n\n if (this.settings.retries) {\n this.client = this.client.compose(retry({\n methods: retriableMethods,\n ...this.settings.retries,\n retry: (err, context, callback) => {\n const { counter } = context.state;\n /* c8 ignore next */\n const maxRetries = context.opts.retryOptions?.maxRetries ?? 5;\n\n const retryToUse = this.settings.retries?.retry ?? defaultRetryMethod;\n const callbackWithLogging = (result?: Error | null) => {\n if (!result) {\n this.#logger.warn(`Request retry attempt ${counter}/${maxRetries}`, { err });\n }\n callback(result);\n };\n retryToUse(err, context, callbackWithLogging);\n },\n }));\n }\n\n if (this.settings.autoDecompress) {\n this.client = this.client.compose(decompress({ skipErrorResponses: false }));\n }\n\n this.client = this.client.compose(outbreakInterceptor);\n }\n\n #createBaseUrl(): void {\n const fullUrl = resolveServiceUrl(this.settings);\n const urlObj = new URL(fullUrl);\n this.settings.baseURL = urlObj.origin;\n this.settings.basePath = urlObj.pathname !== '/' ? urlObj.pathname : '';\n }\n\n #getSignal(currentSignal?: AbortSignal, timeout?: number): { signal?: AbortSignal; timeoutSignal?: AbortSignal; } {\n if (!currentSignal && !timeout) {\n return { signal: undefined, timeoutSignal: undefined };\n }\n\n if (currentSignal && !timeout) {\n return { signal: currentSignal, timeoutSignal: undefined };\n }\n\n const timeoutSignal = AbortSignal.timeout(timeout!);\n if (!currentSignal && timeout) {\n return { signal: timeoutSignal, timeoutSignal };\n }\n\n return { signal: AbortSignal.any([currentSignal!, timeoutSignal]), timeoutSignal };\n }\n\n #prepareRequestBody(body: any, headers: Record<string, string>): string | Buffer | FormData | undefined {\n if (body === undefined || body === null) {\n return undefined;\n }\n\n // Handle FormData instances automatically\n if (isFormData(body)) {\n if (!headers['Content-Type'] && !headers['content-type']) {\n headers['Content-Type'] = 'application/x-www-form-urlencoded';\n }\n return body;\n }\n\n if (typeof body === 'object' && !Buffer.isBuffer(body)) {\n // Check if Content-Type is set to application/x-www-form-urlencoded\n const contentType = headers['Content-Type'] || headers['content-type'];\n if (contentType && /application\\/x-www-form-urlencoded/i.test(contentType)) {\n // Encode as URL parameters\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(body)) {\n if (Array.isArray(value)) {\n value.forEach(v => params.append(key, String(v)));\n } else if (value !== undefined && value !== null) {\n // eslint-disable-next-line @typescript-eslint/no-base-to-string\n params.append(key, String(value));\n }\n }\n return params.toString();\n }\n // Default to JSON\n if (!headers['Content-Type'] && !headers['content-type']) {\n headers['Content-Type'] = 'application/json';\n }\n return JSON.stringify(body);\n } else if (typeof body === 'string') {\n return body;\n } else if (Buffer.isBuffer(body)) {\n return body;\n }\n return String(body);\n }\n\n async #request<T = any>(\n method: Dispatcher.HttpMethod,\n url: string,\n body?: any,\n config: RequestConfig = {},\n ): Promise<RapidoHttpClientResponse<T>> {\n const mergedConfig = mergeConfig(this.settings, config);\n\n if (mergedConfig.signal?.aborted) {\n this.#logger.error(`Request aborted before start: ${createRequestString(method, mergedConfig.baseURL, url)}`);\n throw new errors.RequestAbortedError(mergedConfig.signal.reason?.message || 'Request aborted', { cause: mergedConfig.signal.reason });\n }\n\n const headers: Record<string, string> = { ...mergedConfig.headers };\n const requestBody = this.#prepareRequestBody(body, headers);\n\n const { origin, path } = parseUrlAndOrigin(url, mergedConfig.baseURL!, mergedConfig.basePath);\n const requestInfo = { method, baseURL: origin, url };\n\n if (mergedConfig.params) {\n for (const [key, value] of Object.entries(mergedConfig.params)) {\n if (Array.isArray(value) && !key.endsWith('[]')) {\n mergedConfig.params[`${key}[]`] = value;\n delete mergedConfig.params[key];\n }\n }\n }\n\n const { signal, timeoutSignal } = this.#getSignal(mergedConfig.signal, mergedConfig.timeout);\n\n try {\n this.#logger.info(`Start Request: ${createRequestString(method, origin, path)}`);\n\n const requestOptions: Dispatcher.RequestOptions = {\n path,\n origin,\n method,\n signal,\n headers,\n body: requestBody,\n query: mergedConfig.params,\n };\n\n const response = await this.client.request(requestOptions);\n\n if (response.statusCode >= 400) {\n const data = await this.#parseResponse<T>(response, config.responseSchema);\n this.#logger.error(`Finish Request with error ${createRequestString(method, origin, path)}`, {\n status: response.statusCode,\n data,\n });\n\n throw new errors.RapidoHttpClientError<T>(requestBody, response, data);\n }\n\n this.#logger.info(`Finish Request: ${createRequestString(method, origin, path)}`);\n\n const data = response.statusCode !== 204 ? await this.#parseResponse<T>(response, config.responseSchema) : undefined as T;\n\n return {\n data,\n status: response.statusCode,\n statusText: '',\n headers: response.headers as Record<string, string | string[]>,\n config: requestInfo,\n };\n } catch (error) {\n if (timeoutSignal && timeoutSignal.reason === error) {\n // If the timeoutSignal aborted (config timeout), throw a timeout-specific error\n this.#logger.error(`Request timeout: ${createRequestString(method, origin, path)}`);\n const error = new errors.UndiciError(`Request timeout after ${mergedConfig.timeout}ms`, { cause: timeoutSignal.reason });\n error.code = 'UND_ERR_TIMEOUT';\n throw error;\n }\n if (signal && signal.reason === error) {\n // If the user's signal aborted, throw RequestAbortedError\n this.#logger.error(`Request aborted: ${createRequestString(method, origin, path)}`);\n throw new errors.RequestAbortedError(signal.reason.message, { cause: signal.reason });\n }\n\n this.#logger.error(`Finish Request with error ${createRequestString(method, origin, path)}`, { error });\n throw error;\n }\n }\n\n async #parseResponse<T>(response: Dispatcher.ResponseData, schema?: ZodType): Promise<T> {\n const contentType = (response.headers['content-type'] as string) || '';\n const bodyText = await response.body.text();\n\n if (!bodyText) {\n return undefined as T;\n }\n\n if (/text/i.test(contentType)) {\n return bodyText as T;\n }\n\n let json: unknown;\n try {\n json = JSON.parse(bodyText);\n } catch {\n return bodyText as T;\n }\n return (schema ? schema.parse(json) : json) as T;\n }\n\n /**\n * @param url The endpoint URL to send the request to.\n * @param options Additional options to include in the request payload.\n * @returns A promise that resolves to an array of all results.\n */\n async getAllPages<T>(url: string, options: RequestConfig = {}): Promise<T[]> {\n const currentResult: T[] = [];\n let resultsInFirstPage: number | null = null;\n let lastResultsSize: number | null = null;\n\n const localOptions = { ...options, params: { ...options.params, page: 1 } };\n // Make sure even if `options` had `params` we initialize `page` to 1.\n localOptions.params.page ||= 1;\n\n while ((localOptions.params.page === 1 || lastResultsSize === resultsInFirstPage) && lastResultsSize !== 0) {\n const { data } = await this.get<T[]>(url, localOptions as Omit<RequestConfig, 'responseSchema'>);\n lastResultsSize = data.length;\n if (localOptions.params.page === 1) {\n resultsInFirstPage = data.length;\n }\n\n localOptions.params.page += 1;\n Array.prototype.push.apply(currentResult, data);\n }\n\n return currentResult;\n }\n\n /**\n * Fetches all pages from a paginated API endpoint until all results are retrieved\n * or an optional page limit is reached.\n *\n * @param url The endpoint URL to send the request to.\n * @param options Additional options to include in the request payload.\n * @param pageLimit The maximum number of pages to fetch. Set to -1 for no limit.\n * @returns A promise that resolves to an array of all results.\n */\n async getAllPagesFromQueryEndpoint<T>(url: string, options: object = {}, pageLimit = 100): Promise<T[]> {\n const currentResult: T[] = [];\n\n let moreResultsToLoad = true;\n let page = 1;\n while (moreResultsToLoad && (pageLimit === -1 || page < pageLimit)) {\n const { data } = await this.post<{ rows: T[]; count: number; }>(url, {\n ...options,\n page,\n });\n const { rows, count } = data;\n page += 1;\n Array.prototype.push.apply(currentResult, rows);\n moreResultsToLoad = currentResult.length < count;\n }\n\n return currentResult;\n }\n\n async close(): Promise<void> {\n await this.client.close();\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.close();\n }\n}\n\nexport default RapidoHttpClient;\nexport { errors };\n"],"mappings":"iNAEA,MAAM,EAA6B,OAAO,IAAI,6BAA6B,kCAI7CA,EAAa,kCACLA,EAAa,0CACbA,EAAa,2CACZA,EAAa,wCACjBA,EAAa,iCAChBA,EAAa,qCACNA,EAAa,+CACVA,EAAa,8CACjBA,EAAa,yCACdA,EAAa,uDACEA,EAAa,uEACZA,EAAa,0DAC3BA,EAAa,yCAChBA,EAAa,gCACnBA,EAAa,gCACPA,EAAa,qDACEA,EAAa,mDAC9BA,EAAa,+CACAA,EAAa,iDACxBA,EAAa,+CACJA,EAAa,oDACjBA,EAAa,uBAE7C,SAAS,EAAwB,EAAyD,CAC/F,OAAO,OAAO,GAAU,YAAY,GAAkB,KAA8B,8BAG/E,MAAM,UAAiCA,EAAa,aAAc,CAGvE,YACE,EACA,EACA,EACA,CACA,MAAM,cAAc,EAAS,aAAc,EAAS,WAAY,CAAE,QAAS,EAAS,QAAS,OAAM,CAAC,CAHpF,KAAA,SAAA,EACA,KAAA,KAAA,EAGhB,KAAK,OAAS,EAAS,WAEvB,OAAO,eAAe,KAAM,EAA4B,CACtD,MAAO,GACP,WAAY,GACZ,SAAU,GACX,CAAC,CAGJ,OAAc,wBAAwB,EAAyD,CAC7F,OAAO,EAAwB,EAAM,sCCpD3C,MAAaG,EAAgC,OAAO,iBAAiB,CCGxDC,EAA+D,GAAY,SAAkC,EAAM,EAAS,CACvI,IAAM,EAAsB,GAAmB,CAAC,QAChD,GAAI,EAAqB,CAEvB,IAAIC,EACJ,AAIE,EAJE,MAAM,QAAQ,EAAK,QAAQ,CAEnB,OAAO,YAAY,EAAK,QAAoD,CAE5E,CAAE,GAAG,EAAK,QAA8C,CAIpE,IAAK,GAAM,CAAC,EAAQ,KAAU,EACxB,OAAO,GAAU,UAAmB,IAAU,SAGlD,EAAQ,GAAU,GAEpB,EAAK,QAAU,EAEjB,OAAO,EAAS,EAAM,EAAQ,ECN1B,CAAE,eAAgB,EAClB,CAAE,QAAO,QAAO,cAAe,EAkC/BC,EAA4C,CAAC,MAAO,OAAQ,UAAW,MAAO,SAAU,QAAS,QAAQ,CAE/G,IAAa,EAAb,KAA8B,CAC5B,GAMA,IAAwB,EAAa,EAA8D,CACjG,OAAO,MAAA,EAAiB,MAAO,EAAK,IAAA,GAAW,EAAO,CAKxD,KAAyB,EAAa,EAAY,EAA8D,CAC9G,OAAO,MAAA,EAAiB,OAAQ,EAAK,EAAM,EAAO,CAKpD,OAA2B,EAAa,EAA8D,CACpG,OAAO,MAAA,EAAiB,SAAU,EAAK,IAAA,GAAW,EAAO,CAK3D,KAAyB,EAAa,EAA8D,CAClG,OAAO,MAAA,EAAiB,OAAQ,EAAK,IAAA,GAAW,EAAO,CAKzD,IAAwB,EAAa,EAAY,EAA8D,CAC7G,OAAO,MAAA,EAAiB,MAAO,EAAK,EAAM,EAAO,CAKnD,MAA0B,EAAa,EAAY,EAA8D,CAC/G,OAAO,MAAA,EAAiB,QAAS,EAAK,EAAM,EAAO,CAKrD,QAA4B,EAAa,EAA8D,CACrG,OAAO,MAAA,EAAiB,UAAW,EAAK,IAAA,GAAW,EAAO,CAK5D,MAA0B,EAAa,EAAY,EAA8D,CAC/G,OAAO,MAAA,EAAiB,QAAS,EAAK,EAAM,EAAO,CAGrD,YAAY,EAAoC,CAC9C,MAAA,EAAe,EAAS,OACxB,KAAK,SAAW,EACd,EAAS,eAAiB,EAAgC,EAC1D,EACD,CACD,MAAA,GAAqB,CAKrB,IAAM,EAAmB,WAAmB,kCAE5C,KAAK,OAAS,KAAK,SAAS,IAAmB,GAAmB,IAAI,EAAM,CAC1E,QAAS,CACP,QAAS,KAAK,SAAS,QACxB,CACD,iBAAkB,KAAK,SAAS,iBAChC,oBAAqB,KAAK,SAAS,oBACnC,YAAa,KAAK,SAAS,YAC5B,CAAC,CAEE,KAAK,SAAS,QAChB,KAAK,OAAS,KAAK,OAAO,QAAQ,EAAM,CACtC,MAAO,IAAI,EAAY,iBAAiB,CACtC,GAAG,KAAK,SAAS,MAClB,CAAC,CACH,CAAC,CAAC,EAGD,KAAK,SAAS,UAChB,KAAK,OAAS,KAAK,OAAO,QAAQ,EAAM,CACtC,QAAS,EACT,GAAG,KAAK,SAAS,QACjB,OAAQ,EAAK,EAAS,IAAa,CACjC,GAAM,CAAE,WAAY,EAAQ,MAEtB,EAAa,EAAQ,KAAK,cAAc,YAAc,GAEzC,KAAK,SAAS,SAAS,OAAS,GAOxC,EAAK,EANa,GAA0B,CAChD,GACH,MAAA,EAAa,KAAK,yBAAyB,EAAQ,GAAG,IAAc,CAAE,MAAK,CAAC,CAE9E,EAAS,EAAO,EAE2B,EAEhD,CAAC,CAAC,EAGD,KAAK,SAAS,iBAChB,KAAK,OAAS,KAAK,OAAO,QAAQ,EAAW,CAAE,mBAAoB,GAAO,CAAC,CAAC,EAG9E,KAAK,OAAS,KAAK,OAAO,QAAQ,EAAoB,CAGxD,IAAuB,CACrB,IAAM,EAAU,EAAkB,KAAK,SAAS,CAC1C,EAAS,IAAI,IAAI,EAAQ,CAC/B,KAAK,SAAS,QAAU,EAAO,OAC/B,KAAK,SAAS,SAAW,EAAO,WAAa,IAAwB,GAAlB,EAAO,SAG5D,GAAW,EAA6B,EAA0E,CAChH,GAAI,CAAC,GAAiB,CAAC,EACrB,MAAO,CAAE,OAAQ,IAAA,GAAW,cAAe,IAAA,GAAW,CAGxD,GAAI,GAAiB,CAAC,EACpB,MAAO,CAAE,OAAQ,EAAe,cAAe,IAAA,GAAW,CAG5D,IAAM,EAAgB,YAAY,QAAQ,EAAS,CAKnD,MAJI,CAAC,GAAiB,EACb,CAAE,OAAQ,EAAe,gBAAe,CAG1C,CAAE,OAAQ,YAAY,IAAI,CAAC,EAAgB,EAAc,CAAC,CAAE,gBAAe,CAGpF,GAAoB,EAAW,EAAyE,CAClG,MAA+B,KAKnC,IAAI,EAAW,EAAK,CAIlB,MAHI,CAAC,EAAQ,iBAAmB,CAAC,EAAQ,kBACvC,EAAQ,gBAAkB,qCAErB,EAGT,GAAI,OAAO,GAAS,UAAY,CAAC,OAAO,SAAS,EAAK,CAAE,CAEtD,IAAM,EAAc,EAAQ,iBAAmB,EAAQ,gBACvD,GAAI,GAAe,sCAAsC,KAAK,EAAY,CAAE,CAE1E,IAAM,EAAS,IAAI,gBACnB,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,EAAK,CACzC,MAAM,QAAQ,EAAM,CACtB,EAAM,QAAQ,GAAK,EAAO,OAAO,EAAK,OAAO,EAAE,CAAC,CAAC,CACxC,GAAiC,MAE1C,EAAO,OAAO,EAAK,OAAO,EAAM,CAAC,CAGrC,OAAO,EAAO,UAAU,CAM1B,MAHI,CAAC,EAAQ,iBAAmB,CAAC,EAAQ,kBACvC,EAAQ,gBAAkB,oBAErB,KAAK,UAAU,EAAK,SAClB,OAAO,GAAS,SACzB,OAAO,UACE,OAAO,SAAS,EAAK,CAC9B,OAAO,EAET,OAAO,OAAO,EAAK,EAGrB,MAAA,EACE,EACA,EACA,EACA,EAAwB,EAAE,CACY,CACtC,IAAM,EAAe,EAAY,KAAK,SAAU,EAAO,CAEvD,GAAI,EAAa,QAAQ,QAEvB,MADA,MAAA,EAAa,MAAM,iCAAiC,EAAoB,EAAQ,EAAa,QAAS,EAAI,GAAG,CACvG,IAAI,EAAO,oBAAoB,EAAa,OAAO,QAAQ,SAAW,kBAAmB,CAAE,MAAO,EAAa,OAAO,OAAQ,CAAC,CAGvI,IAAMI,EAAkC,CAAE,GAAG,EAAa,QAAS,CAC7D,EAAc,MAAA,EAAyB,EAAM,EAAQ,CAErD,CAAE,SAAQ,QAAS,EAAkB,EAAK,EAAa,QAAU,EAAa,SAAS,CACvF,EAAc,CAAE,SAAQ,QAAS,EAAQ,MAAK,CAEpD,GAAI,EAAa,WACV,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,EAAa,OAAO,CACxD,MAAM,QAAQ,EAAM,EAAI,CAAC,EAAI,SAAS,KAAK,GAC7C,EAAa,OAAO,GAAG,EAAI,KAAO,EAClC,OAAO,EAAa,OAAO,IAKjC,GAAM,CAAE,SAAQ,iBAAkB,MAAA,EAAgB,EAAa,OAAQ,EAAa,QAAQ,CAE5F,GAAI,CACF,MAAA,EAAa,KAAK,kBAAkB,EAAoB,EAAQ,EAAQ,EAAK,GAAG,CAEhF,IAAMG,EAA4C,CAChD,OACA,SACA,SACA,SACA,UACA,KAAM,EACN,MAAO,EAAa,OACrB,CAEK,EAAW,MAAM,KAAK,OAAO,QAAQ,EAAe,CAE1D,GAAI,EAAS,YAAc,IAAK,CAC9B,IAAM,EAAO,MAAM,MAAA,EAAuB,EAAU,EAAO,eAAe,CAM1E,MALA,MAAA,EAAa,MAAM,6BAA6B,EAAoB,EAAQ,EAAQ,EAAK,GAAI,CAC3F,OAAQ,EAAS,WACjB,OACD,CAAC,CAEI,IAAI,EAAO,sBAAyB,EAAa,EAAU,EAAK,CAOxE,OAJA,MAAA,EAAa,KAAK,mBAAmB,EAAoB,EAAQ,EAAQ,EAAK,GAAG,CAI1E,CACL,KAHW,EAAS,aAAe,IAAsE,IAAA,GAAhE,MAAM,MAAA,EAAuB,EAAU,EAAO,eAAe,CAItG,OAAQ,EAAS,WACjB,WAAY,GACZ,QAAS,EAAS,QAClB,OAAQ,EACT,OACM,EAAO,CACd,GAAI,GAAiB,EAAc,SAAW,EAAO,CAEnD,MAAA,EAAa,MAAM,oBAAoB,EAAoB,EAAQ,EAAQ,EAAK,GAAG,CACnF,IAAME,EAAQ,IAAI,EAAO,YAAY,yBAAyB,EAAa,QAAQ,IAAK,CAAE,MAAO,EAAc,OAAQ,CAAC,CAExH,KADA,GAAM,KAAO,kBACPA,EASR,MAPI,GAAU,EAAO,SAAW,GAE9B,MAAA,EAAa,MAAM,oBAAoB,EAAoB,EAAQ,EAAQ,EAAK,GAAG,CAC7E,IAAI,EAAO,oBAAoB,EAAO,OAAO,QAAS,CAAE,MAAO,EAAO,OAAQ,CAAC,GAGvF,MAAA,EAAa,MAAM,6BAA6B,EAAoB,EAAQ,EAAQ,EAAK,GAAI,CAAE,QAAO,CAAC,CACjG,IAIV,MAAA,EAAwB,EAAmC,EAA8B,CACvF,IAAM,EAAe,EAAS,QAAQ,iBAA8B,GAC9D,EAAW,MAAM,EAAS,KAAK,MAAM,CAE3C,GAAI,CAAC,EACH,OAGF,GAAI,QAAQ,KAAK,EAAY,CAC3B,OAAO,EAGT,IAAIC,EACJ,GAAI,CACF,EAAO,KAAK,MAAM,EAAS,MACrB,CACN,OAAO,EAET,OAAQ,EAAS,EAAO,MAAM,EAAK,CAAG,EAQxC,MAAM,YAAe,EAAa,EAAyB,EAAE,CAAgB,CAC3E,IAAMC,EAAqB,EAAE,CACzBC,EAAoC,KACpCC,EAAiC,KAE/B,EAAe,CAAE,GAAG,EAAS,OAAQ,CAAE,GAAG,EAAQ,OAAQ,KAAM,EAAG,CAAE,CAI3E,IAFA,EAAa,OAAO,OAAS,GAErB,EAAa,OAAO,OAAS,GAAK,IAAoB,IAAuB,IAAoB,GAAG,CAC1G,GAAM,CAAE,QAAS,MAAM,KAAK,IAAS,EAAK,EAAsD,CAChG,EAAkB,EAAK,OACnB,EAAa,OAAO,OAAS,IAC/B,EAAqB,EAAK,QAG5B,EAAa,OAAO,MAAQ,EAC5B,MAAM,UAAU,KAAK,MAAM,EAAe,EAAK,CAGjD,OAAO,EAYT,MAAM,6BAAgC,EAAa,EAAkB,EAAE,CAAE,EAAY,IAAmB,CACtG,IAAMF,EAAqB,EAAE,CAEzB,EAAoB,GACpB,EAAO,EACX,KAAO,IAAsB,IAAc,IAAM,EAAO,IAAY,CAClE,GAAM,CAAE,QAAS,MAAM,KAAK,KAAoC,EAAK,CACnE,GAAG,EACH,OACD,CAAC,CACI,CAAE,OAAM,SAAU,EACxB,GAAQ,EACR,MAAM,UAAU,KAAK,MAAM,EAAe,EAAK,CAC/C,EAAoB,EAAc,OAAS,EAG7C,OAAO,EAGT,MAAM,OAAuB,CAC3B,MAAM,KAAK,OAAO,OAAO,CAG3B,MAAO,OAAO,eAA+B,CAC3C,MAAM,KAAK,OAAO,GAItB,EAAe"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["undiciErrors","response: Dispatcher.ResponseData","data: T","testAgentParam: unique symbol","outbreakInterceptor: Dispatcher.DispatcherComposeInterceptor","headers: Record<string, string | string[]>","retriableMethods: Dispatcher.HttpMethod[]","#logger","#request","#createBaseUrl","headers: Record<string, string>","#prepareRequestBody","#getSignal","requestOptions: Dispatcher.RequestOptions","#parseResponse","error","json: unknown","currentResult: T[]","resultsInFirstPage: number | null","lastResultsSize: number | null"],"sources":["../src/errors.ts","../src/testingHelper.ts","../src/outbreakInterceptor.ts","../src/index.ts"],"sourcesContent":["import { STATUS_CODES } from 'node:http';\nimport { type Dispatcher, errors as undiciErrors } from 'undici';\n\nconst RapidoHttpClientErrorBrand = Symbol.for('RapidoHttpClientErrorBrand');\n\n// eslint-disable-next-line @typescript-eslint/no-namespace\nexport namespace errors {\n export import UndiciError = undiciErrors.UndiciError;\n export import ConnectTimeoutError = undiciErrors.ConnectTimeoutError;\n export import HeadersTimeoutError = undiciErrors.HeadersTimeoutError;\n export import HeadersOverflowError = undiciErrors.HeadersOverflowError;\n export import BodyTimeoutError = undiciErrors.BodyTimeoutError;\n export import ResponseError = undiciErrors.ResponseError;\n export import InvalidArgumentError = undiciErrors.InvalidArgumentError;\n export import InvalidReturnValueError = undiciErrors.InvalidReturnValueError;\n export import RequestAbortedError = undiciErrors.RequestAbortedError;\n export import InformationalError = undiciErrors.InformationalError;\n export import RequestContentLengthMismatchError = undiciErrors.RequestContentLengthMismatchError;\n export import ResponseContentLengthMismatchError = undiciErrors.ResponseContentLengthMismatchError;\n export import ClientDestroyedError = undiciErrors.ClientDestroyedError;\n export import ClientClosedError = undiciErrors.ClientClosedError;\n export import SocketError = undiciErrors.SocketError;\n export import NotSupportedError = undiciErrors.NotSupportedError;\n export import BalancedPoolMissingUpstreamError = undiciErrors.BalancedPoolMissingUpstreamError;\n export import HTTPParserError = undiciErrors.HTTPParserError;\n export import ResponseExceededMaxSizeError = undiciErrors.ResponseExceededMaxSizeError;\n export import RequestRetryError = undiciErrors.RequestRetryError;\n export import SecureProxyConnectionError = undiciErrors.SecureProxyConnectionError;\n export import MaxOriginsReachedError = undiciErrors.MaxOriginsReachedError;\n\n export function isRapidoHttpClientError(value: unknown): value is RapidoHttpClientError<unknown> {\n return typeof value === 'object' && value !== null && RapidoHttpClientErrorBrand in value;\n }\n\n export class RapidoHttpClientError<T> extends undiciErrors.ResponseError {\n public readonly status: number;\n public readonly statusText: string;\n\n constructor(\n body: ConstructorParameters<typeof undiciErrors.ResponseError>[2]['body'],\n public readonly response: Dispatcher.ResponseData,\n public readonly data: T,\n ) {\n super(`HTTP Error ${response.statusCode}`, response.statusCode, { headers: response.headers, body });\n this.status = response.statusCode;\n this.statusText = STATUS_CODES[response.statusCode] || 'Unknown Status';\n\n Object.defineProperty(this, RapidoHttpClientErrorBrand, {\n value: true,\n enumerable: false,\n writable: false,\n });\n }\n\n public static isRapidoHttpClientError(value: unknown): value is RapidoHttpClientError<unknown> {\n return isRapidoHttpClientError(value);\n }\n }\n}\n","export const testAgentParam: unique symbol = Symbol('testAgentParam');\n","import type { Dispatcher } from 'undici';\nimport { getCurrentContext } from '@autofleet/outbreak';\n\nexport const outbreakInterceptor: Dispatcher.DispatcherComposeInterceptor = dispatch => function outbreakInterceptorInner(opts, handler) {\n const currentTraceContext = getCurrentContext().context;\n if (currentTraceContext) {\n // Convert headers to object format if they're in array format\n let headers: Record<string, string | string[]>;\n if (Array.isArray(opts.headers)) {\n /* c8 ignore next */\n headers = Object.fromEntries(opts.headers as unknown as [string, string | string[]][]);\n } else {\n headers = { ...opts.headers as Record<string, string | string[]> };\n }\n\n // Inject outbreak trace headers\n for (const [header, value] of currentTraceContext) {\n if (typeof value === 'symbol' || typeof value === 'undefined') {\n continue;\n }\n headers[header] = value as string;\n }\n opts.headers = headers;\n }\n return dispatch(opts, handler);\n};\n","import { STATUS_CODES } from 'node:http';\nimport type { ZodType, output } from 'zod';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\nimport type CacheHandler from 'undici/types/cache-interceptor';\nimport Undici, { Agent, type Dispatcher, interceptors } from 'undici';\nimport { errors } from './errors';\nimport { testAgentParam } from './testingHelper';\nimport { outbreakInterceptor } from './outbreakInterceptor';\nimport {\n createRequestString,\n defaultRetryMethod,\n defaultSettings,\n defaultSettingsWithDecompress,\n isFormData,\n mergeConfig,\n parseUrlAndOrigin,\n resolveServiceUrl,\n} from './utils';\n\nconst { cacheStores } = Undici;\nconst { cache, retry, decompress } = interceptors;\n\nexport interface RapidoHttpClientSettings {\n [testAgentParam]?: Dispatcher;\n /** The MS name to be used to resolve the base URL using K8S service discovery. Ignored if `serviceUrl` is provided as well. */\n serviceName?: string;\n /** The base URL to be used. Overrides `serviceName` when provided. */\n serviceUrl?: string;\n logger: LoggerInstanceManager;\n /** The request timeout in milliseconds. @default 10_000 */\n timeout?: number;\n /** Settings for retrying requests on failure */\n retries?: interceptors.RetryInterceptorOpts;\n /** Common headers applied to all requests of this `RapidoHttpClient` instance. */\n headers?: Record<string, string> | string[][];\n keepAliveTimeout?: number;\n keepAliveMaxTimeout?: number;\n /** The amount of concurrent connections to be maintained using a connection pool. @default 10 */\n connections?: number;\n /** The settings for caching requests */\n cache?: CacheHandler.MemoryCacheStoreOpts;\n /** Whether to automatically decompress response bodies. When `true` will automatically add an `Accept-Encoding` header. @default false */\n autoDecompress?: boolean;\n /**\n * A function to validate the HTTP response status code.\n * Return `true` to consider the status valid, `false` otherwise.\n *\n * When status is invalid, a {@link errors.RapidoHttpClientError `RapidoHttpClientError`} will be thrown.\n *\n * @example (status) => status >= 200 && status < 300\n * @example (status) => status !== 401\n *\n * @default (status) => status < 400\n */\n validateStatus?(status: number): boolean;\n}\n\nexport interface RequestConfig {\n params?: Record<string, any>;\n body?: any;\n /** The request timeout in milliseconds. This overrides the client's instance setting @default 10_000 */\n timeout?: number;\n /** Headers applied to this request, appending on the client's common headers. */\n headers?: Record<string, string>;\n /** An `AbortSignal` which should abort the request and it's parsing when aborted. */\n signal?: AbortSignal;\n /** A Zod schema defining what the response should look like. This also parses error responses. */\n responseSchema?: ZodType;\n /**\n * A function to validate the HTTP response status code.\n * Return `true` to consider the status valid, `false` otherwise.\n *\n * When status is invalid, a {@link errors.RapidoHttpClientError `RapidoHttpClientError`} will be thrown.\n *\n * @example (status) => status >= 200 && status < 300\n * @example (status) => status !== 401\n *\n * @default (status) => status < 400\n */\n validateStatus?(status: number): boolean;\n}\n\nexport interface RapidoHttpClientResponse<T = any> {\n data: T;\n status: number;\n statusText: string;\n headers: Record<string, string | string[]>;\n config: RequestConfig & { url: string; method: string; baseURL?: string; };\n}\n\nconst retriableMethods: Dispatcher.HttpMethod[] = ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE', 'QUERY'];\n\nexport class RapidoHttpClient {\n readonly #logger: LoggerInstanceManager;\n private readonly settings: RapidoHttpClientSettings & { baseURL?: string; basePath?: string; };\n public readonly client: Dispatcher.ComposedDispatcher;\n\n public get<T = unknown>(url: string, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public get<T extends ZodType>(url: string, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public get<T = unknown>(url: string, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('GET', url, undefined, config);\n }\n\n public post<T = unknown>(url: string, body?: any, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public post<T extends ZodType>(url: string, body: any, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public post<T = unknown>(url: string, body?: any, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('POST', url, body, config);\n }\n\n public delete<T = unknown>(url: string, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public delete<T extends ZodType>(url: string, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public delete<T = unknown>(url: string, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('DELETE', url, undefined, config);\n }\n\n public head<T = unknown>(url: string, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public head<T extends ZodType>(url: string, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public head<T = unknown>(url: string, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('HEAD', url, undefined, config);\n }\n\n public put<T = unknown>(url: string, body?: any, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public put<T extends ZodType>(url: string, body: any, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public put<T = unknown>(url: string, body?: any, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('PUT', url, body, config);\n }\n\n public patch<T = unknown>(url: string, body?: any, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public patch<T extends ZodType>(url: string, body: any, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public patch<T = unknown>(url: string, body?: any, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('PATCH', url, body, config);\n }\n\n public options<T = unknown>(url: string, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public options<T extends ZodType>(url: string, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public options<T = unknown>(url: string, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('OPTIONS', url, undefined, config);\n }\n\n public query<T = unknown>(url: string, body?: any, config?: Omit<RequestConfig, 'responseSchema'> & { responseSchema?: never; }): Promise<RapidoHttpClientResponse<T>>;\n public query<T extends ZodType>(url: string, body: any, config: RequestConfig & { responseSchema: T; }): Promise<RapidoHttpClientResponse<output<T>>>;\n public query<T = unknown>(url: string, body?: any, config?: RequestConfig): Promise<RapidoHttpClientResponse<T>> {\n return this.#request<T>('QUERY', url, body, config);\n }\n\n constructor(settings: RapidoHttpClientSettings) {\n this.#logger = settings.logger;\n this.settings = mergeConfig<typeof defaultSettings, Partial<typeof settings>>(\n settings.autoDecompress ? defaultSettingsWithDecompress : defaultSettings,\n settings,\n ) as RapidoHttpClientSettings & { baseURL?: string; };\n this.#createBaseUrl();\n\n // In test mode, use the test dispatcher directly (MockAgent or MockPool) Otherwise, create an Agent which creates Client Pools\n // Check for global mock agent first (for testing), then test param, then create real Agent\n /* c8 ignore next */\n const globalMockAgent = (globalThis as any).__RAPIDO_HTTP_CLIENT_MOCK_AGENT__;\n /* c8 ignore next */\n this.client = this.settings[testAgentParam] ?? globalMockAgent ?? new Agent({\n connect: {\n timeout: this.settings.timeout,\n },\n keepAliveTimeout: this.settings.keepAliveTimeout,\n keepAliveMaxTimeout: this.settings.keepAliveMaxTimeout,\n connections: this.settings.connections,\n });\n\n if (this.settings.cache) {\n this.client = this.client.compose(cache({\n store: new cacheStores.MemoryCacheStore({\n ...this.settings.cache,\n }),\n }));\n }\n\n if (this.settings.retries) {\n this.client = this.client.compose(retry({\n methods: retriableMethods,\n ...this.settings.retries,\n retry: (err, context, callback) => {\n const { counter } = context.state;\n /* c8 ignore next */\n const maxRetries = context.opts.retryOptions?.maxRetries ?? 5;\n\n const retryToUse = this.settings.retries?.retry ?? defaultRetryMethod;\n const callbackWithLogging = (result?: Error | null) => {\n if (!result) {\n const { method, origin, path } = context.opts;\n const baseUrl = (!origin || typeof origin === 'string') ? origin : origin.origin;\n this.#logger.warn(`Request retry attempt ${counter}/${maxRetries}: ${createRequestString(method, baseUrl, path)}`, { err });\n }\n callback(result);\n };\n retryToUse(err, context, callbackWithLogging);\n },\n }));\n }\n\n if (this.settings.autoDecompress) {\n this.client = this.client.compose(decompress({ skipErrorResponses: false }));\n }\n\n this.client = this.client.compose(outbreakInterceptor);\n }\n\n #createBaseUrl(): void {\n const fullUrl = resolveServiceUrl(this.settings);\n const urlObj = new URL(fullUrl);\n this.settings.baseURL = urlObj.origin;\n this.settings.basePath = urlObj.pathname !== '/' ? urlObj.pathname : '';\n }\n\n #getSignal(currentSignal?: AbortSignal, timeout?: number): { signal?: AbortSignal; timeoutSignal?: AbortSignal; } {\n if (!currentSignal && !timeout) {\n return { signal: undefined, timeoutSignal: undefined };\n }\n\n if (currentSignal && !timeout) {\n return { signal: currentSignal, timeoutSignal: undefined };\n }\n\n const timeoutSignal = AbortSignal.timeout(timeout!);\n if (!currentSignal && timeout) {\n return { signal: timeoutSignal, timeoutSignal };\n }\n\n return { signal: AbortSignal.any([currentSignal!, timeoutSignal]), timeoutSignal };\n }\n\n #prepareRequestBody(body: any, headers: Record<string, string>): string | Buffer | FormData | undefined {\n if (body === undefined || body === null) {\n return undefined;\n }\n\n // Handle FormData instances automatically\n if (isFormData(body)) {\n if (!headers['Content-Type'] && !headers['content-type']) {\n headers['Content-Type'] = 'application/x-www-form-urlencoded';\n }\n return body;\n }\n\n if (typeof body === 'object' && !Buffer.isBuffer(body)) {\n // Check if Content-Type is set to application/x-www-form-urlencoded\n const contentType = headers['Content-Type'] || headers['content-type'];\n if (contentType && /application\\/x-www-form-urlencoded/i.test(contentType)) {\n // Encode as URL parameters\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(body)) {\n if (Array.isArray(value)) {\n value.forEach(v => params.append(key, String(v)));\n } else if (value !== undefined && value !== null) {\n // eslint-disable-next-line @typescript-eslint/no-base-to-string\n params.append(key, String(value));\n }\n }\n return params.toString();\n }\n // Default to JSON\n if (!headers['Content-Type'] && !headers['content-type']) {\n headers['Content-Type'] = 'application/json';\n }\n return JSON.stringify(body);\n } else if (typeof body === 'string') {\n return body;\n } else if (Buffer.isBuffer(body)) {\n return body;\n }\n return String(body);\n }\n\n async #request<T = any>(\n method: Dispatcher.HttpMethod,\n url: string,\n body?: any,\n config: RequestConfig = {},\n ): Promise<RapidoHttpClientResponse<T>> {\n const mergedConfig = mergeConfig(this.settings, config);\n\n if (mergedConfig.signal?.aborted) {\n this.#logger.error(`Request aborted before start: ${createRequestString(method, mergedConfig.baseURL, url)}`);\n throw new errors.RequestAbortedError(mergedConfig.signal.reason?.message || 'Request aborted', { cause: mergedConfig.signal.reason });\n }\n\n const headers: Record<string, string> = { ...mergedConfig.headers };\n const requestBody = this.#prepareRequestBody(body, headers);\n\n const { origin, path } = parseUrlAndOrigin(url, mergedConfig.baseURL!, mergedConfig.basePath);\n const requestInfo = { method, baseURL: origin, url };\n\n if (mergedConfig.params) {\n for (const [key, value] of Object.entries(mergedConfig.params)) {\n if (Array.isArray(value) && !key.endsWith('[]')) {\n mergedConfig.params[`${key}[]`] = value;\n delete mergedConfig.params[key];\n }\n }\n }\n\n const { signal, timeoutSignal } = this.#getSignal(mergedConfig.signal, mergedConfig.timeout);\n\n try {\n this.#logger.info(`Start Request: ${createRequestString(method, origin, path)}`);\n\n const requestOptions: Dispatcher.RequestOptions = {\n path,\n origin,\n method,\n signal,\n headers,\n body: requestBody,\n query: mergedConfig.params,\n };\n\n const response = await this.client.request(requestOptions);\n\n const isStatusValid = mergedConfig.validateStatus ?? (() => response.statusCode < 400);\n if (!isStatusValid(response.statusCode)) {\n const data = await this.#parseResponse<T>(response, config.responseSchema);\n this.#logger.error(`Finish Request with error ${createRequestString(method, origin, path)}`, {\n status: response.statusCode,\n data,\n });\n\n throw new errors.RapidoHttpClientError<T>(requestBody, response, data);\n }\n\n this.#logger.info(`Finish Request: ${createRequestString(method, origin, path)}`);\n\n const data = response.statusCode !== 204 ? await this.#parseResponse<T>(response, config.responseSchema) : undefined as T;\n\n return {\n data,\n status: response.statusCode,\n statusText: STATUS_CODES[response.statusCode] || '',\n headers: response.headers as Record<string, string | string[]>,\n config: requestInfo,\n };\n } catch (error) {\n if (timeoutSignal && timeoutSignal.reason === error) {\n // If the timeoutSignal aborted (config timeout), throw a timeout-specific error\n this.#logger.error(`Request timeout: ${createRequestString(method, origin, path)}`);\n const error = new errors.UndiciError(`Request timeout after ${mergedConfig.timeout}ms`, { cause: timeoutSignal.reason });\n error.code = 'UND_ERR_TIMEOUT';\n throw error;\n }\n if (signal && signal.reason === error) {\n // If the user's signal aborted, throw RequestAbortedError\n this.#logger.error(`Request aborted: ${createRequestString(method, origin, path)}`);\n throw new errors.RequestAbortedError(signal.reason.message, { cause: signal.reason });\n }\n\n this.#logger.error(`Finish Request with error ${createRequestString(method, origin, path)}`, { error });\n throw error;\n }\n }\n\n async #parseResponse<T>(response: Dispatcher.ResponseData, schema?: ZodType): Promise<T> {\n const contentType = (response.headers['content-type'] as string) || '';\n const bodyText = await response.body.text();\n\n if (!bodyText) {\n return undefined as T;\n }\n\n if (/text/i.test(contentType)) {\n return bodyText as T;\n }\n\n let json: unknown;\n try {\n json = JSON.parse(bodyText);\n } catch {\n return bodyText as T;\n }\n return (schema ? schema.parse(json) : json) as T;\n }\n\n /**\n * @param url The endpoint URL to send the request to.\n * @param options Additional options to include in the request payload.\n * @returns A promise that resolves to an array of all results.\n */\n async getAllPages<T>(url: string, options: RequestConfig = {}): Promise<T[]> {\n const currentResult: T[] = [];\n let resultsInFirstPage: number | null = null;\n let lastResultsSize: number | null = null;\n\n const localOptions = { ...options, params: { ...options.params, page: 1 } };\n // Make sure even if `options` had `params` we initialize `page` to 1.\n localOptions.params.page ||= 1;\n\n while ((localOptions.params.page === 1 || lastResultsSize === resultsInFirstPage) && lastResultsSize !== 0) {\n const { data } = await this.get<T[]>(url, localOptions as Omit<RequestConfig, 'responseSchema'>);\n lastResultsSize = data.length;\n if (localOptions.params.page === 1) {\n resultsInFirstPage = data.length;\n }\n\n localOptions.params.page += 1;\n Array.prototype.push.apply(currentResult, data);\n }\n\n return currentResult;\n }\n\n /**\n * Fetches all pages from a paginated API endpoint until all results are retrieved\n * or an optional page limit is reached.\n *\n * @param url The endpoint URL to send the request to.\n * @param options Additional options to include in the request payload.\n * @param pageLimit The maximum number of pages to fetch. Set to -1 for no limit.\n * @returns A promise that resolves to an array of all results.\n */\n async getAllPagesFromQueryEndpoint<T>(url: string, options: object = {}, pageLimit = 100): Promise<T[]> {\n const currentResult: T[] = [];\n\n let moreResultsToLoad = true;\n let page = 1;\n while (moreResultsToLoad && (pageLimit === -1 || page < pageLimit)) {\n const { data } = await this.post<{ rows: T[]; count: number; }>(url, {\n ...options,\n page,\n });\n const { rows, count } = data;\n page += 1;\n Array.prototype.push.apply(currentResult, rows);\n moreResultsToLoad = currentResult.length < count;\n }\n\n return currentResult;\n }\n\n async close(): Promise<void> {\n await this.client.close();\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.close();\n }\n}\n\nexport default RapidoHttpClient;\nexport { errors };\n"],"mappings":"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"}
|
package/dist/testing.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require(`./utils-
|
|
1
|
+
const e=require(`./utils-Dh5SqGdK.cjs`),t=require(`./index.cjs`);let n=require(`undici`);function r({blockNonMockedNetwork:t,collectCallHistory:r}={}){let i=new n.MockAgent({enableCallHistory:r??!1});(t??!0)&&i.disableNetConnect(),globalThis.__RAPIDO_HTTP_CLIENT_MOCK_AGENT__=i;let a=new Map,o=new Map,s=e=>{let t=new URL(e),n=t.origin,r=t.pathname===`/`?``:t.pathname.replace(/\/$/,``),s=n+r;if(!a.has(s)){let e=i.get(n);a.set(s,e),o.set(s,r)}return{pool:a.get(s),basePath:o.get(s)||``}},c=(e,t)=>{if(typeof e==`string`)return{path:t+e};let n={...e};if(typeof n.path==`string`&&(n.path=t+n.path),n.body&&typeof n.body==`object`&&!(n.body instanceof RegExp)&&typeof n.body!=`function`){let e=n.headers,t=e?.[`Content-Type`]||e?.[`content-type`];if(t&&/application\/x-www-form-urlencoded/i.test(t)){let e=new URLSearchParams;for(let[t,r]of Object.entries(n.body))Array.isArray(r)?r.forEach(n=>e.append(t,String(n))):r!=null&&e.append(t,String(r));n.body=e.toString()}else n.body=JSON.stringify(n.body)}if(n.query)for(let[e,t]of Object.entries(n.query))Array.isArray(t)&&!e.endsWith(`[]`)&&(n.query[`${e}[]`]=t,delete n.query[e]);return n};return{intercept:t=>{let{pool:n,basePath:r}=s(typeof t==`string`?t:e.c(t));return{get:e=>n.intercept({...c(e,r),method:`GET`}),head:e=>n.intercept({...c(e,r),method:`HEAD`}),delete:e=>n.intercept({...c(e,r),method:`DELETE`}),options:e=>n.intercept({...c(e,r),method:`OPTIONS`}),put:e=>n.intercept({...c(e,r),method:`PUT`}),post:e=>n.intercept({...c(e,r),method:`POST`}),patch:e=>n.intercept({...c(e,r),method:`PATCH`}),query:e=>n.intercept({...c(e,r),method:`QUERY`})}},getPool:t=>s(typeof t==`string`?t:e.c(t)).pool,cleanup:async()=>{delete globalThis.__RAPIDO_HTTP_CLIENT_MOCK_AGENT__,a.clear(),o.clear(),await i.close(),i.clearCallHistory()},get calls(){return i.getCallHistory()?.calls()??[]},clearCallHistory:()=>i.clearCallHistory(),enableCallHistory:()=>{i.enableCallHistory()},disableCallHistory:()=>{i.disableCallHistory()},mockAgent:i}}exports.setupRapidoHttpClientMock=r;
|
|
2
2
|
//# sourceMappingURL=testing.cjs.map
|
package/dist/testing.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.cjs","names":["MockAgent","resolveServiceUrl"],"sources":["../src/testing.ts"],"sourcesContent":["import { MockAgent, type MockCallHistoryLog, type MockPool } from 'undici';\nimport { resolveServiceUrl } from './utils';\nimport type { MockInterceptor } from 'undici/types/mock-interceptor';\n\ninterface SetupRapidoHttpClientMockOptions {\n /** Whether to block any real network requests that are not mocked. @default true */\n blockNonMockedNetwork?: boolean;\n /** Collect call history for inspection/assertion. @default false */\n collectCallHistory?: boolean;\n}\n\ninterface InterceptOptions {\n /** Path to intercept on. */\n path: string | RegExp | ((path: string) => boolean);\n /** Body to intercept on. Plain objects will be JSON.stringify'd automatically. */\n body?: string | Record<string, any> | RegExp | ((body: string) => boolean);\n /** Headers to intercept on. */\n headers?: Record<string, string | RegExp | ((body: string) => boolean)> | ((headers: Record<string, string>) => boolean);\n /** Query params to intercept on */\n query?: Record<string, any>;\n}\n\nexport interface RapidoHttpClientMocker {\n /**\n * Set up intercepts for a specific origin with a chainable API.\n *\n * Returns an object with HTTP method helpers that create MockInterceptors.\n * Each method accepts either a path string or an InterceptOptions object for advanced matching.\n * The returned MockInterceptor supports `.reply()`, `.replyWithError()`, `.times()`, and `.persist()`.\n *\n * @param target - Either a URL string, or an object with serviceName or serviceUrl\n *\n * @example\n * ```typescript\n * // Simple path matching\n * mock.intercept('https://api.example.com')\n * .get('/users')\n * .reply(200, { users: [] }, { headers: { 'content-type': 'application/json' } });\n *\n * // Advanced matching with body, headers, query params\n * mock.intercept('https://api.example.com')\n * .post({ path: '/users', body: JSON.stringify({ name: 'John' }) })\n * .reply(201, { id: 1, name: 'John' }, { headers: { 'content-type': 'application/json' } });\n *\n * // Using serviceName (reads from env)\n * mock.intercept({ serviceName: 'USER' })\n * .get({ path: '/profile', query: { id: '123' } })\n * .reply(200, { id: 123, name: 'Alice' }, { headers: { 'content-type': 'application/json' } });\n *\n * // Chain with times() or persist()\n * mock.intercept({ serviceUrl: 'https://api.example.com' })\n * .get('/data')\n * .reply(200, { data: [] }, { headers: { 'content-type': 'application/json' } })\n * .times(3); // Only mock 3 times\n *\n * // Mock errors\n * mock.intercept('https://api.example.com')\n * .get('/error')\n * .replyWithError(new Error('Network failure'))\n * .persist(); // Always throw this error\n * ```\n */\n intercept: (target: string | { serviceName: string; } | { serviceUrl: string; }) => {\n get: (options: string | InterceptOptions) => MockInterceptor;\n head: (options: string | InterceptOptions) => MockInterceptor;\n delete: (options: string | InterceptOptions) => MockInterceptor;\n options: (options: string | InterceptOptions) => MockInterceptor;\n put: (options: string | InterceptOptions) => MockInterceptor;\n post: (options: string | InterceptOptions) => MockInterceptor;\n patch: (options: string | InterceptOptions) => MockInterceptor;\n query: (options: string | InterceptOptions) => MockInterceptor;\n };\n /**\n * Get the mock pool for a specific origin for advanced usage.\n * This gives you direct access to the undici MockPool for full control.\n *\n * @param target - Either a URL string, or an object with serviceName or serviceUrl\n */\n getPool: (target: string | { serviceName: string; } | { serviceUrl: string; }) => MockPool;\n /**\n * Clean up the global mock.\n * Should be called in afterEach or after tests complete.\n */\n cleanup: () => Promise<void>;\n /**\n * Access the underlying mock agent for advanced usage\n */\n mockAgent: MockAgent<MockAgent.Options>;\n /** Call history log of all intercepted requests. */\n calls: MockCallHistoryLog[];\n /** Clear the call history log. */\n clearCallHistory: () => void;\n /** Enable call history collection. */\n enableCallHistory: () => void;\n /** Disable call history collection. */\n disableCallHistory: () => void;\n}\n\n/**\n * Global mock setup for RapidoHttpClient.\n *\n * This provides a nock-like global mocking experience where you can set up\n * mock responses before creating RapidoHttpClient instances. The mock is automatically\n * used by any RapidoHttpClient instance created after setupGlobalMock() is called.\n *\n * @example\n * ```typescript\n * import { setupRapidoHttpClientMock } from '@autofleet/rapido-http-client/testing';\n * import { RapidoHttpClient } from '@autofleet/rapido-http-client';\n *\n * // In your test setup (e.g., beforeEach)\n * const mock = setupRapidoHttpClientMock();\n *\n * // Set up mocks for specific origins\n * const apiMock = mock.intercept('https://api.example.com');\n * apiMock.get('/users').reply(200, { users: [] });\n * apiMock.post('/users', { name: 'John' }).reply(201, { id: 1, name: 'John' });\n *\n * // Or use the pool directly for more control\n * mock.getPool('https://api.example.com')\n * .intercept({ path: '/data', method: 'GET' })\n * .reply(200, { data: 'test' }, { headers: { 'content-type': 'application/json' } });\n *\n * // Create RapidoHttpClient instances normally - mocks are automatically used!\n * const client = new RapidoHttpClient({\n * serviceUrl: 'https://api.example.com',\n * logger: mockLogger,\n * });\n *\n * // Use the client - it will use the mocked responses\n * const response = await client.get('/users');\n *\n * // Clean up in afterEach\n * await mock.cleanup();\n * ```\n */\nexport function setupRapidoHttpClientMock({ blockNonMockedNetwork, collectCallHistory }: SetupRapidoHttpClientMockOptions = {}): RapidoHttpClientMocker {\n const mockAgent = new MockAgent({ enableCallHistory: collectCallHistory ?? false });\n if (blockNonMockedNetwork ?? true) {\n mockAgent.disableNetConnect();\n }\n\n // Store the mock agent globally for RapidoHttpClient to use\n (globalThis as any).__RAPIDO_HTTP_CLIENT_MOCK_AGENT__ = mockAgent;\n\n const mockPools = new Map<string, MockPool>();\n const basePaths = new Map<string, string>(); // Track base paths for each origin\n\n const getOrCreatePool = (urlOrOrigin: string): { pool: MockPool; basePath: string; } => {\n // MockAgent requires just the origin, not the full URL with path\n // Extract origin and pathname if a full URL is provided\n const url = new URL(urlOrOrigin);\n const origin = url.origin;\n // Remove trailing slash from base path to avoid double slashes\n const basePath = url.pathname === '/' ? '' : url.pathname.replace(/\\/$/, '');\n\n if (!mockPools.has(origin)) {\n const pool = mockAgent.get<MockPool>(origin);\n mockPools.set(origin, pool);\n }\n\n // Store or retrieve the base path for this origin\n if (basePath && !basePaths.has(origin)) {\n basePaths.set(origin, basePath);\n }\n\n return { pool: mockPools.get(origin)!, basePath: basePaths.get(origin) || '' };\n };\n\n const normalizeOptions = (options: string | InterceptOptions, basePath: string): MockInterceptor.Options => {\n if (typeof options === 'string') {\n return { path: basePath + options };\n }\n const normalized = { ...options };\n\n // Prepend base path to the path option\n if (typeof normalized.path === 'string') {\n normalized.path = basePath + normalized.path;\n }\n\n // Auto-encode object bodies based on Content-Type\n if (normalized.body && typeof normalized.body === 'object' && !(normalized.body instanceof RegExp) && typeof normalized.body !== 'function') {\n // Check if Content-Type is application/x-www-form-urlencoded\n const headers = normalized.headers as Record<string, string> | undefined;\n const contentType = headers?.['Content-Type'] || headers?.['content-type'];\n\n if (contentType && /application\\/x-www-form-urlencoded/i.test(contentType)) {\n // URL-encode the body (same logic as RapidoHttpClient's #prepareRequestBody)\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(normalized.body)) {\n if (Array.isArray(value)) {\n value.forEach(v => params.append(key, String(v)));\n } else if (value !== undefined && value !== null) {\n params.append(key, String(value));\n }\n }\n normalized.body = params.toString();\n } else {\n // Default to JSON\n normalized.body = JSON.stringify(normalized.body);\n }\n }\n // Auto-append [] to array query params\n if (normalized.query) {\n for (const [key, value] of Object.entries(normalized.query)) {\n if (Array.isArray(value) && !key.endsWith('[]')) {\n normalized.query[`${key}[]`] = value;\n delete normalized.query[key];\n }\n }\n }\n return normalized as MockInterceptor.Options;\n };\n\n return {\n intercept: (target: string | { serviceName: string; } | { serviceUrl: string; }) => {\n const origin = typeof target === 'string' ? target : resolveServiceUrl(target);\n const { pool, basePath } = getOrCreatePool(origin);\n\n return {\n get: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'GET' }),\n head: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'HEAD' }),\n delete: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'DELETE' }),\n options: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'OPTIONS' }),\n put: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'PUT' }),\n post: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'POST' }),\n patch: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'PATCH' }),\n query: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'QUERY' }),\n };\n },\n\n getPool: (target: string | { serviceName: string; } | { serviceUrl: string; }): MockPool => {\n const origin = typeof target === 'string' ? target : resolveServiceUrl(target);\n return getOrCreatePool(origin).pool;\n },\n cleanup: async (): Promise<void> => {\n delete (globalThis as any).__RAPIDO_HTTP_CLIENT_MOCK_AGENT__;\n mockPools.clear();\n basePaths.clear();\n await mockAgent.close();\n mockAgent.clearCallHistory();\n },\n\n get calls(): MockCallHistoryLog[] {\n return mockAgent.getCallHistory()?.calls() ?? [];\n },\n\n clearCallHistory: (): void => mockAgent.clearCallHistory(),\n\n enableCallHistory: (): void => {\n mockAgent.enableCallHistory();\n },\n\n disableCallHistory: (): void => {\n mockAgent.disableCallHistory();\n },\n\n mockAgent,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"testing.cjs","names":["MockAgent","resolveServiceUrl"],"sources":["../src/testing.ts"],"sourcesContent":["import { MockAgent, type MockCallHistoryLog, type MockPool } from 'undici';\nimport { resolveServiceUrl } from './utils';\nimport type { MockInterceptor } from 'undici/types/mock-interceptor';\n\ninterface SetupRapidoHttpClientMockOptions {\n /** Whether to block any real network requests that are not mocked. @default true */\n blockNonMockedNetwork?: boolean;\n /** Collect call history for inspection/assertion. @default false */\n collectCallHistory?: boolean;\n}\n\ninterface InterceptOptions {\n /** Path to intercept on. */\n path: string | RegExp | ((path: string) => boolean);\n /** Body to intercept on. Plain objects will be JSON.stringify'd automatically. */\n body?: string | Record<string, any> | RegExp | ((body: string) => boolean);\n /** Headers to intercept on. */\n headers?: Record<string, string | RegExp | ((body: string) => boolean)> | ((headers: Record<string, string>) => boolean);\n /** Query params to intercept on */\n query?: Record<string, any>;\n}\n\nexport interface RapidoHttpClientMocker {\n /**\n * Set up intercepts for a specific origin with a chainable API.\n *\n * Returns an object with HTTP method helpers that create MockInterceptors.\n * Each method accepts either a path string or an InterceptOptions object for advanced matching.\n * The returned MockInterceptor supports `.reply()`, `.replyWithError()`, `.times()`, and `.persist()`.\n *\n * @param target - Either a URL string, or an object with serviceName or serviceUrl\n *\n * @example\n * ```typescript\n * // Simple path matching\n * mock.intercept('https://api.example.com')\n * .get('/users')\n * .reply(200, { users: [] }, { headers: { 'content-type': 'application/json' } });\n *\n * // Advanced matching with body, headers, query params\n * mock.intercept('https://api.example.com')\n * .post({ path: '/users', body: JSON.stringify({ name: 'John' }) })\n * .reply(201, { id: 1, name: 'John' }, { headers: { 'content-type': 'application/json' } });\n *\n * // Using serviceName (reads from env)\n * mock.intercept({ serviceName: 'USER' })\n * .get({ path: '/profile', query: { id: '123' } })\n * .reply(200, { id: 123, name: 'Alice' }, { headers: { 'content-type': 'application/json' } });\n *\n * // Chain with times() or persist()\n * mock.intercept({ serviceUrl: 'https://api.example.com' })\n * .get('/data')\n * .reply(200, { data: [] }, { headers: { 'content-type': 'application/json' } })\n * .times(3); // Only mock 3 times\n *\n * // Mock errors\n * mock.intercept('https://api.example.com')\n * .get('/error')\n * .replyWithError(new Error('Network failure'))\n * .persist(); // Always throw this error\n * ```\n */\n intercept: (target: string | { serviceName: string; } | { serviceUrl: string; }) => {\n get: (options: string | InterceptOptions) => MockInterceptor;\n head: (options: string | InterceptOptions) => MockInterceptor;\n delete: (options: string | InterceptOptions) => MockInterceptor;\n options: (options: string | InterceptOptions) => MockInterceptor;\n put: (options: string | InterceptOptions) => MockInterceptor;\n post: (options: string | InterceptOptions) => MockInterceptor;\n patch: (options: string | InterceptOptions) => MockInterceptor;\n query: (options: string | InterceptOptions) => MockInterceptor;\n };\n /**\n * Get the mock pool for a specific origin for advanced usage.\n * This gives you direct access to the undici MockPool for full control.\n *\n * @param target - Either a URL string, or an object with serviceName or serviceUrl\n */\n getPool: (target: string | { serviceName: string; } | { serviceUrl: string; }) => MockPool;\n /**\n * Clean up the global mock.\n * Should be called in afterEach or after tests complete.\n */\n cleanup: () => Promise<void>;\n /**\n * Access the underlying mock agent for advanced usage\n */\n mockAgent: MockAgent<MockAgent.Options>;\n /** Call history log of all intercepted requests. */\n calls: MockCallHistoryLog[];\n /** Clear the call history log. */\n clearCallHistory: () => void;\n /** Enable call history collection. */\n enableCallHistory: () => void;\n /** Disable call history collection. */\n disableCallHistory: () => void;\n}\n\n/**\n * Global mock setup for RapidoHttpClient.\n *\n * This provides a nock-like global mocking experience where you can set up\n * mock responses before creating RapidoHttpClient instances. The mock is automatically\n * used by any RapidoHttpClient instance created after setupGlobalMock() is called.\n *\n * @example\n * ```typescript\n * import { setupRapidoHttpClientMock } from '@autofleet/rapido-http-client/testing';\n * import { RapidoHttpClient } from '@autofleet/rapido-http-client';\n *\n * // In your test setup (e.g., beforeEach)\n * const mock = setupRapidoHttpClientMock();\n *\n * // Set up mocks for specific origins\n * const apiMock = mock.intercept('https://api.example.com');\n * apiMock.get('/users').reply(200, { users: [] });\n * apiMock.post('/users', { name: 'John' }).reply(201, { id: 1, name: 'John' });\n *\n * // Or use the pool directly for more control\n * mock.getPool('https://api.example.com')\n * .intercept({ path: '/data', method: 'GET' })\n * .reply(200, { data: 'test' }, { headers: { 'content-type': 'application/json' } });\n *\n * // Create RapidoHttpClient instances normally - mocks are automatically used!\n * const client = new RapidoHttpClient({\n * serviceUrl: 'https://api.example.com',\n * logger: mockLogger,\n * });\n *\n * // Use the client - it will use the mocked responses\n * const response = await client.get('/users');\n *\n * // Clean up in afterEach\n * await mock.cleanup();\n * ```\n */\nexport function setupRapidoHttpClientMock({ blockNonMockedNetwork, collectCallHistory }: SetupRapidoHttpClientMockOptions = {}): RapidoHttpClientMocker {\n const mockAgent = new MockAgent({ enableCallHistory: collectCallHistory ?? false });\n if (blockNonMockedNetwork ?? true) {\n mockAgent.disableNetConnect();\n }\n\n // Store the mock agent globally for RapidoHttpClient to use\n (globalThis as any).__RAPIDO_HTTP_CLIENT_MOCK_AGENT__ = mockAgent;\n\n const mockPools = new Map<string, MockPool>();\n const basePaths = new Map<string, string>(); // Track base paths for each origin\n\n const getOrCreatePool = (urlOrOrigin: string): { pool: MockPool; basePath: string; } => {\n // MockAgent requires just the origin, not the full URL with path\n // Extract origin and pathname if a full URL is provided\n const url = new URL(urlOrOrigin);\n const origin = url.origin;\n // Remove trailing slash from base path to avoid double slashes\n const basePath = url.pathname === '/' ? '' : url.pathname.replace(/\\/$/, '');\n\n // Use the full URL (origin + basePath) as the key to support different pools for different paths\n const poolKey = origin + basePath;\n\n if (!mockPools.has(poolKey)) {\n const pool = mockAgent.get<MockPool>(origin);\n mockPools.set(poolKey, pool);\n basePaths.set(poolKey, basePath);\n }\n\n return { pool: mockPools.get(poolKey)!, basePath: basePaths.get(poolKey) || '' };\n };\n\n const normalizeOptions = (options: string | InterceptOptions, basePath: string): MockInterceptor.Options => {\n if (typeof options === 'string') {\n return { path: basePath + options };\n }\n const normalized = { ...options };\n\n // Prepend base path to the path option\n if (typeof normalized.path === 'string') {\n normalized.path = basePath + normalized.path;\n }\n\n // Auto-encode object bodies based on Content-Type\n if (normalized.body && typeof normalized.body === 'object' && !(normalized.body instanceof RegExp) && typeof normalized.body !== 'function') {\n // Check if Content-Type is application/x-www-form-urlencoded\n const headers = normalized.headers as Record<string, string> | undefined;\n const contentType = headers?.['Content-Type'] || headers?.['content-type'];\n\n if (contentType && /application\\/x-www-form-urlencoded/i.test(contentType)) {\n // URL-encode the body (same logic as RapidoHttpClient's #prepareRequestBody)\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(normalized.body)) {\n if (Array.isArray(value)) {\n value.forEach(v => params.append(key, String(v)));\n } else if (value !== undefined && value !== null) {\n params.append(key, String(value));\n }\n }\n normalized.body = params.toString();\n } else {\n // Default to JSON\n normalized.body = JSON.stringify(normalized.body);\n }\n }\n // Auto-append [] to array query params\n if (normalized.query) {\n for (const [key, value] of Object.entries(normalized.query)) {\n if (Array.isArray(value) && !key.endsWith('[]')) {\n normalized.query[`${key}[]`] = value;\n delete normalized.query[key];\n }\n }\n }\n return normalized as MockInterceptor.Options;\n };\n\n return {\n intercept: (target: string | { serviceName: string; } | { serviceUrl: string; }) => {\n const origin = typeof target === 'string' ? target : resolveServiceUrl(target);\n const { pool, basePath } = getOrCreatePool(origin);\n\n return {\n get: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'GET' }),\n head: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'HEAD' }),\n delete: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'DELETE' }),\n options: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'OPTIONS' }),\n put: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'PUT' }),\n post: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'POST' }),\n patch: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'PATCH' }),\n query: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'QUERY' }),\n };\n },\n\n getPool: (target: string | { serviceName: string; } | { serviceUrl: string; }): MockPool => {\n const origin = typeof target === 'string' ? target : resolveServiceUrl(target);\n return getOrCreatePool(origin).pool;\n },\n cleanup: async (): Promise<void> => {\n delete (globalThis as any).__RAPIDO_HTTP_CLIENT_MOCK_AGENT__;\n mockPools.clear();\n basePaths.clear();\n await mockAgent.close();\n mockAgent.clearCallHistory();\n },\n\n get calls(): MockCallHistoryLog[] {\n return mockAgent.getCallHistory()?.calls() ?? [];\n },\n\n clearCallHistory: (): void => mockAgent.clearCallHistory(),\n\n enableCallHistory: (): void => {\n mockAgent.enableCallHistory();\n },\n\n disableCallHistory: (): void => {\n mockAgent.disableCallHistory();\n },\n\n mockAgent,\n };\n}\n"],"mappings":"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"}
|
package/dist/testing.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{c as e}from"./utils-
|
|
1
|
+
import{c as e}from"./utils-CN26OYva.js";import{MockAgent as t}from"undici";function n({blockNonMockedNetwork:n,collectCallHistory:r}={}){let i=new t({enableCallHistory:r??!1});(n??!0)&&i.disableNetConnect(),globalThis.__RAPIDO_HTTP_CLIENT_MOCK_AGENT__=i;let a=new Map,o=new Map,s=e=>{let t=new URL(e),n=t.origin,r=t.pathname===`/`?``:t.pathname.replace(/\/$/,``),s=n+r;if(!a.has(s)){let e=i.get(n);a.set(s,e),o.set(s,r)}return{pool:a.get(s),basePath:o.get(s)||``}},c=(e,t)=>{if(typeof e==`string`)return{path:t+e};let n={...e};if(typeof n.path==`string`&&(n.path=t+n.path),n.body&&typeof n.body==`object`&&!(n.body instanceof RegExp)&&typeof n.body!=`function`){let e=n.headers,t=e?.[`Content-Type`]||e?.[`content-type`];if(t&&/application\/x-www-form-urlencoded/i.test(t)){let e=new URLSearchParams;for(let[t,r]of Object.entries(n.body))Array.isArray(r)?r.forEach(n=>e.append(t,String(n))):r!=null&&e.append(t,String(r));n.body=e.toString()}else n.body=JSON.stringify(n.body)}if(n.query)for(let[e,t]of Object.entries(n.query))Array.isArray(t)&&!e.endsWith(`[]`)&&(n.query[`${e}[]`]=t,delete n.query[e]);return n};return{intercept:t=>{let{pool:n,basePath:r}=s(typeof t==`string`?t:e(t));return{get:e=>n.intercept({...c(e,r),method:`GET`}),head:e=>n.intercept({...c(e,r),method:`HEAD`}),delete:e=>n.intercept({...c(e,r),method:`DELETE`}),options:e=>n.intercept({...c(e,r),method:`OPTIONS`}),put:e=>n.intercept({...c(e,r),method:`PUT`}),post:e=>n.intercept({...c(e,r),method:`POST`}),patch:e=>n.intercept({...c(e,r),method:`PATCH`}),query:e=>n.intercept({...c(e,r),method:`QUERY`})}},getPool:t=>s(typeof t==`string`?t:e(t)).pool,cleanup:async()=>{delete globalThis.__RAPIDO_HTTP_CLIENT_MOCK_AGENT__,a.clear(),o.clear(),await i.close(),i.clearCallHistory()},get calls(){return i.getCallHistory()?.calls()??[]},clearCallHistory:()=>i.clearCallHistory(),enableCallHistory:()=>{i.enableCallHistory()},disableCallHistory:()=>{i.disableCallHistory()},mockAgent:i}}export{n as setupRapidoHttpClientMock};
|
|
2
2
|
//# sourceMappingURL=testing.js.map
|
package/dist/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.js","names":[],"sources":["../src/testing.ts"],"sourcesContent":["import { MockAgent, type MockCallHistoryLog, type MockPool } from 'undici';\nimport { resolveServiceUrl } from './utils';\nimport type { MockInterceptor } from 'undici/types/mock-interceptor';\n\ninterface SetupRapidoHttpClientMockOptions {\n /** Whether to block any real network requests that are not mocked. @default true */\n blockNonMockedNetwork?: boolean;\n /** Collect call history for inspection/assertion. @default false */\n collectCallHistory?: boolean;\n}\n\ninterface InterceptOptions {\n /** Path to intercept on. */\n path: string | RegExp | ((path: string) => boolean);\n /** Body to intercept on. Plain objects will be JSON.stringify'd automatically. */\n body?: string | Record<string, any> | RegExp | ((body: string) => boolean);\n /** Headers to intercept on. */\n headers?: Record<string, string | RegExp | ((body: string) => boolean)> | ((headers: Record<string, string>) => boolean);\n /** Query params to intercept on */\n query?: Record<string, any>;\n}\n\nexport interface RapidoHttpClientMocker {\n /**\n * Set up intercepts for a specific origin with a chainable API.\n *\n * Returns an object with HTTP method helpers that create MockInterceptors.\n * Each method accepts either a path string or an InterceptOptions object for advanced matching.\n * The returned MockInterceptor supports `.reply()`, `.replyWithError()`, `.times()`, and `.persist()`.\n *\n * @param target - Either a URL string, or an object with serviceName or serviceUrl\n *\n * @example\n * ```typescript\n * // Simple path matching\n * mock.intercept('https://api.example.com')\n * .get('/users')\n * .reply(200, { users: [] }, { headers: { 'content-type': 'application/json' } });\n *\n * // Advanced matching with body, headers, query params\n * mock.intercept('https://api.example.com')\n * .post({ path: '/users', body: JSON.stringify({ name: 'John' }) })\n * .reply(201, { id: 1, name: 'John' }, { headers: { 'content-type': 'application/json' } });\n *\n * // Using serviceName (reads from env)\n * mock.intercept({ serviceName: 'USER' })\n * .get({ path: '/profile', query: { id: '123' } })\n * .reply(200, { id: 123, name: 'Alice' }, { headers: { 'content-type': 'application/json' } });\n *\n * // Chain with times() or persist()\n * mock.intercept({ serviceUrl: 'https://api.example.com' })\n * .get('/data')\n * .reply(200, { data: [] }, { headers: { 'content-type': 'application/json' } })\n * .times(3); // Only mock 3 times\n *\n * // Mock errors\n * mock.intercept('https://api.example.com')\n * .get('/error')\n * .replyWithError(new Error('Network failure'))\n * .persist(); // Always throw this error\n * ```\n */\n intercept: (target: string | { serviceName: string; } | { serviceUrl: string; }) => {\n get: (options: string | InterceptOptions) => MockInterceptor;\n head: (options: string | InterceptOptions) => MockInterceptor;\n delete: (options: string | InterceptOptions) => MockInterceptor;\n options: (options: string | InterceptOptions) => MockInterceptor;\n put: (options: string | InterceptOptions) => MockInterceptor;\n post: (options: string | InterceptOptions) => MockInterceptor;\n patch: (options: string | InterceptOptions) => MockInterceptor;\n query: (options: string | InterceptOptions) => MockInterceptor;\n };\n /**\n * Get the mock pool for a specific origin for advanced usage.\n * This gives you direct access to the undici MockPool for full control.\n *\n * @param target - Either a URL string, or an object with serviceName or serviceUrl\n */\n getPool: (target: string | { serviceName: string; } | { serviceUrl: string; }) => MockPool;\n /**\n * Clean up the global mock.\n * Should be called in afterEach or after tests complete.\n */\n cleanup: () => Promise<void>;\n /**\n * Access the underlying mock agent for advanced usage\n */\n mockAgent: MockAgent<MockAgent.Options>;\n /** Call history log of all intercepted requests. */\n calls: MockCallHistoryLog[];\n /** Clear the call history log. */\n clearCallHistory: () => void;\n /** Enable call history collection. */\n enableCallHistory: () => void;\n /** Disable call history collection. */\n disableCallHistory: () => void;\n}\n\n/**\n * Global mock setup for RapidoHttpClient.\n *\n * This provides a nock-like global mocking experience where you can set up\n * mock responses before creating RapidoHttpClient instances. The mock is automatically\n * used by any RapidoHttpClient instance created after setupGlobalMock() is called.\n *\n * @example\n * ```typescript\n * import { setupRapidoHttpClientMock } from '@autofleet/rapido-http-client/testing';\n * import { RapidoHttpClient } from '@autofleet/rapido-http-client';\n *\n * // In your test setup (e.g., beforeEach)\n * const mock = setupRapidoHttpClientMock();\n *\n * // Set up mocks for specific origins\n * const apiMock = mock.intercept('https://api.example.com');\n * apiMock.get('/users').reply(200, { users: [] });\n * apiMock.post('/users', { name: 'John' }).reply(201, { id: 1, name: 'John' });\n *\n * // Or use the pool directly for more control\n * mock.getPool('https://api.example.com')\n * .intercept({ path: '/data', method: 'GET' })\n * .reply(200, { data: 'test' }, { headers: { 'content-type': 'application/json' } });\n *\n * // Create RapidoHttpClient instances normally - mocks are automatically used!\n * const client = new RapidoHttpClient({\n * serviceUrl: 'https://api.example.com',\n * logger: mockLogger,\n * });\n *\n * // Use the client - it will use the mocked responses\n * const response = await client.get('/users');\n *\n * // Clean up in afterEach\n * await mock.cleanup();\n * ```\n */\nexport function setupRapidoHttpClientMock({ blockNonMockedNetwork, collectCallHistory }: SetupRapidoHttpClientMockOptions = {}): RapidoHttpClientMocker {\n const mockAgent = new MockAgent({ enableCallHistory: collectCallHistory ?? false });\n if (blockNonMockedNetwork ?? true) {\n mockAgent.disableNetConnect();\n }\n\n // Store the mock agent globally for RapidoHttpClient to use\n (globalThis as any).__RAPIDO_HTTP_CLIENT_MOCK_AGENT__ = mockAgent;\n\n const mockPools = new Map<string, MockPool>();\n const basePaths = new Map<string, string>(); // Track base paths for each origin\n\n const getOrCreatePool = (urlOrOrigin: string): { pool: MockPool; basePath: string; } => {\n // MockAgent requires just the origin, not the full URL with path\n // Extract origin and pathname if a full URL is provided\n const url = new URL(urlOrOrigin);\n const origin = url.origin;\n // Remove trailing slash from base path to avoid double slashes\n const basePath = url.pathname === '/' ? '' : url.pathname.replace(/\\/$/, '');\n\n if (!mockPools.has(origin)) {\n const pool = mockAgent.get<MockPool>(origin);\n mockPools.set(origin, pool);\n }\n\n // Store or retrieve the base path for this origin\n if (basePath && !basePaths.has(origin)) {\n basePaths.set(origin, basePath);\n }\n\n return { pool: mockPools.get(origin)!, basePath: basePaths.get(origin) || '' };\n };\n\n const normalizeOptions = (options: string | InterceptOptions, basePath: string): MockInterceptor.Options => {\n if (typeof options === 'string') {\n return { path: basePath + options };\n }\n const normalized = { ...options };\n\n // Prepend base path to the path option\n if (typeof normalized.path === 'string') {\n normalized.path = basePath + normalized.path;\n }\n\n // Auto-encode object bodies based on Content-Type\n if (normalized.body && typeof normalized.body === 'object' && !(normalized.body instanceof RegExp) && typeof normalized.body !== 'function') {\n // Check if Content-Type is application/x-www-form-urlencoded\n const headers = normalized.headers as Record<string, string> | undefined;\n const contentType = headers?.['Content-Type'] || headers?.['content-type'];\n\n if (contentType && /application\\/x-www-form-urlencoded/i.test(contentType)) {\n // URL-encode the body (same logic as RapidoHttpClient's #prepareRequestBody)\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(normalized.body)) {\n if (Array.isArray(value)) {\n value.forEach(v => params.append(key, String(v)));\n } else if (value !== undefined && value !== null) {\n params.append(key, String(value));\n }\n }\n normalized.body = params.toString();\n } else {\n // Default to JSON\n normalized.body = JSON.stringify(normalized.body);\n }\n }\n // Auto-append [] to array query params\n if (normalized.query) {\n for (const [key, value] of Object.entries(normalized.query)) {\n if (Array.isArray(value) && !key.endsWith('[]')) {\n normalized.query[`${key}[]`] = value;\n delete normalized.query[key];\n }\n }\n }\n return normalized as MockInterceptor.Options;\n };\n\n return {\n intercept: (target: string | { serviceName: string; } | { serviceUrl: string; }) => {\n const origin = typeof target === 'string' ? target : resolveServiceUrl(target);\n const { pool, basePath } = getOrCreatePool(origin);\n\n return {\n get: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'GET' }),\n head: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'HEAD' }),\n delete: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'DELETE' }),\n options: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'OPTIONS' }),\n put: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'PUT' }),\n post: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'POST' }),\n patch: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'PATCH' }),\n query: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'QUERY' }),\n };\n },\n\n getPool: (target: string | { serviceName: string; } | { serviceUrl: string; }): MockPool => {\n const origin = typeof target === 'string' ? target : resolveServiceUrl(target);\n return getOrCreatePool(origin).pool;\n },\n cleanup: async (): Promise<void> => {\n delete (globalThis as any).__RAPIDO_HTTP_CLIENT_MOCK_AGENT__;\n mockPools.clear();\n basePaths.clear();\n await mockAgent.close();\n mockAgent.clearCallHistory();\n },\n\n get calls(): MockCallHistoryLog[] {\n return mockAgent.getCallHistory()?.calls() ?? [];\n },\n\n clearCallHistory: (): void => mockAgent.clearCallHistory(),\n\n enableCallHistory: (): void => {\n mockAgent.enableCallHistory();\n },\n\n disableCallHistory: (): void => {\n mockAgent.disableCallHistory();\n },\n\n mockAgent,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"testing.js","names":[],"sources":["../src/testing.ts"],"sourcesContent":["import { MockAgent, type MockCallHistoryLog, type MockPool } from 'undici';\nimport { resolveServiceUrl } from './utils';\nimport type { MockInterceptor } from 'undici/types/mock-interceptor';\n\ninterface SetupRapidoHttpClientMockOptions {\n /** Whether to block any real network requests that are not mocked. @default true */\n blockNonMockedNetwork?: boolean;\n /** Collect call history for inspection/assertion. @default false */\n collectCallHistory?: boolean;\n}\n\ninterface InterceptOptions {\n /** Path to intercept on. */\n path: string | RegExp | ((path: string) => boolean);\n /** Body to intercept on. Plain objects will be JSON.stringify'd automatically. */\n body?: string | Record<string, any> | RegExp | ((body: string) => boolean);\n /** Headers to intercept on. */\n headers?: Record<string, string | RegExp | ((body: string) => boolean)> | ((headers: Record<string, string>) => boolean);\n /** Query params to intercept on */\n query?: Record<string, any>;\n}\n\nexport interface RapidoHttpClientMocker {\n /**\n * Set up intercepts for a specific origin with a chainable API.\n *\n * Returns an object with HTTP method helpers that create MockInterceptors.\n * Each method accepts either a path string or an InterceptOptions object for advanced matching.\n * The returned MockInterceptor supports `.reply()`, `.replyWithError()`, `.times()`, and `.persist()`.\n *\n * @param target - Either a URL string, or an object with serviceName or serviceUrl\n *\n * @example\n * ```typescript\n * // Simple path matching\n * mock.intercept('https://api.example.com')\n * .get('/users')\n * .reply(200, { users: [] }, { headers: { 'content-type': 'application/json' } });\n *\n * // Advanced matching with body, headers, query params\n * mock.intercept('https://api.example.com')\n * .post({ path: '/users', body: JSON.stringify({ name: 'John' }) })\n * .reply(201, { id: 1, name: 'John' }, { headers: { 'content-type': 'application/json' } });\n *\n * // Using serviceName (reads from env)\n * mock.intercept({ serviceName: 'USER' })\n * .get({ path: '/profile', query: { id: '123' } })\n * .reply(200, { id: 123, name: 'Alice' }, { headers: { 'content-type': 'application/json' } });\n *\n * // Chain with times() or persist()\n * mock.intercept({ serviceUrl: 'https://api.example.com' })\n * .get('/data')\n * .reply(200, { data: [] }, { headers: { 'content-type': 'application/json' } })\n * .times(3); // Only mock 3 times\n *\n * // Mock errors\n * mock.intercept('https://api.example.com')\n * .get('/error')\n * .replyWithError(new Error('Network failure'))\n * .persist(); // Always throw this error\n * ```\n */\n intercept: (target: string | { serviceName: string; } | { serviceUrl: string; }) => {\n get: (options: string | InterceptOptions) => MockInterceptor;\n head: (options: string | InterceptOptions) => MockInterceptor;\n delete: (options: string | InterceptOptions) => MockInterceptor;\n options: (options: string | InterceptOptions) => MockInterceptor;\n put: (options: string | InterceptOptions) => MockInterceptor;\n post: (options: string | InterceptOptions) => MockInterceptor;\n patch: (options: string | InterceptOptions) => MockInterceptor;\n query: (options: string | InterceptOptions) => MockInterceptor;\n };\n /**\n * Get the mock pool for a specific origin for advanced usage.\n * This gives you direct access to the undici MockPool for full control.\n *\n * @param target - Either a URL string, or an object with serviceName or serviceUrl\n */\n getPool: (target: string | { serviceName: string; } | { serviceUrl: string; }) => MockPool;\n /**\n * Clean up the global mock.\n * Should be called in afterEach or after tests complete.\n */\n cleanup: () => Promise<void>;\n /**\n * Access the underlying mock agent for advanced usage\n */\n mockAgent: MockAgent<MockAgent.Options>;\n /** Call history log of all intercepted requests. */\n calls: MockCallHistoryLog[];\n /** Clear the call history log. */\n clearCallHistory: () => void;\n /** Enable call history collection. */\n enableCallHistory: () => void;\n /** Disable call history collection. */\n disableCallHistory: () => void;\n}\n\n/**\n * Global mock setup for RapidoHttpClient.\n *\n * This provides a nock-like global mocking experience where you can set up\n * mock responses before creating RapidoHttpClient instances. The mock is automatically\n * used by any RapidoHttpClient instance created after setupGlobalMock() is called.\n *\n * @example\n * ```typescript\n * import { setupRapidoHttpClientMock } from '@autofleet/rapido-http-client/testing';\n * import { RapidoHttpClient } from '@autofleet/rapido-http-client';\n *\n * // In your test setup (e.g., beforeEach)\n * const mock = setupRapidoHttpClientMock();\n *\n * // Set up mocks for specific origins\n * const apiMock = mock.intercept('https://api.example.com');\n * apiMock.get('/users').reply(200, { users: [] });\n * apiMock.post('/users', { name: 'John' }).reply(201, { id: 1, name: 'John' });\n *\n * // Or use the pool directly for more control\n * mock.getPool('https://api.example.com')\n * .intercept({ path: '/data', method: 'GET' })\n * .reply(200, { data: 'test' }, { headers: { 'content-type': 'application/json' } });\n *\n * // Create RapidoHttpClient instances normally - mocks are automatically used!\n * const client = new RapidoHttpClient({\n * serviceUrl: 'https://api.example.com',\n * logger: mockLogger,\n * });\n *\n * // Use the client - it will use the mocked responses\n * const response = await client.get('/users');\n *\n * // Clean up in afterEach\n * await mock.cleanup();\n * ```\n */\nexport function setupRapidoHttpClientMock({ blockNonMockedNetwork, collectCallHistory }: SetupRapidoHttpClientMockOptions = {}): RapidoHttpClientMocker {\n const mockAgent = new MockAgent({ enableCallHistory: collectCallHistory ?? false });\n if (blockNonMockedNetwork ?? true) {\n mockAgent.disableNetConnect();\n }\n\n // Store the mock agent globally for RapidoHttpClient to use\n (globalThis as any).__RAPIDO_HTTP_CLIENT_MOCK_AGENT__ = mockAgent;\n\n const mockPools = new Map<string, MockPool>();\n const basePaths = new Map<string, string>(); // Track base paths for each origin\n\n const getOrCreatePool = (urlOrOrigin: string): { pool: MockPool; basePath: string; } => {\n // MockAgent requires just the origin, not the full URL with path\n // Extract origin and pathname if a full URL is provided\n const url = new URL(urlOrOrigin);\n const origin = url.origin;\n // Remove trailing slash from base path to avoid double slashes\n const basePath = url.pathname === '/' ? '' : url.pathname.replace(/\\/$/, '');\n\n // Use the full URL (origin + basePath) as the key to support different pools for different paths\n const poolKey = origin + basePath;\n\n if (!mockPools.has(poolKey)) {\n const pool = mockAgent.get<MockPool>(origin);\n mockPools.set(poolKey, pool);\n basePaths.set(poolKey, basePath);\n }\n\n return { pool: mockPools.get(poolKey)!, basePath: basePaths.get(poolKey) || '' };\n };\n\n const normalizeOptions = (options: string | InterceptOptions, basePath: string): MockInterceptor.Options => {\n if (typeof options === 'string') {\n return { path: basePath + options };\n }\n const normalized = { ...options };\n\n // Prepend base path to the path option\n if (typeof normalized.path === 'string') {\n normalized.path = basePath + normalized.path;\n }\n\n // Auto-encode object bodies based on Content-Type\n if (normalized.body && typeof normalized.body === 'object' && !(normalized.body instanceof RegExp) && typeof normalized.body !== 'function') {\n // Check if Content-Type is application/x-www-form-urlencoded\n const headers = normalized.headers as Record<string, string> | undefined;\n const contentType = headers?.['Content-Type'] || headers?.['content-type'];\n\n if (contentType && /application\\/x-www-form-urlencoded/i.test(contentType)) {\n // URL-encode the body (same logic as RapidoHttpClient's #prepareRequestBody)\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(normalized.body)) {\n if (Array.isArray(value)) {\n value.forEach(v => params.append(key, String(v)));\n } else if (value !== undefined && value !== null) {\n params.append(key, String(value));\n }\n }\n normalized.body = params.toString();\n } else {\n // Default to JSON\n normalized.body = JSON.stringify(normalized.body);\n }\n }\n // Auto-append [] to array query params\n if (normalized.query) {\n for (const [key, value] of Object.entries(normalized.query)) {\n if (Array.isArray(value) && !key.endsWith('[]')) {\n normalized.query[`${key}[]`] = value;\n delete normalized.query[key];\n }\n }\n }\n return normalized as MockInterceptor.Options;\n };\n\n return {\n intercept: (target: string | { serviceName: string; } | { serviceUrl: string; }) => {\n const origin = typeof target === 'string' ? target : resolveServiceUrl(target);\n const { pool, basePath } = getOrCreatePool(origin);\n\n return {\n get: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'GET' }),\n head: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'HEAD' }),\n delete: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'DELETE' }),\n options: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'OPTIONS' }),\n put: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'PUT' }),\n post: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'POST' }),\n patch: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'PATCH' }),\n query: (options: string | InterceptOptions) => pool.intercept({ ...normalizeOptions(options, basePath), method: 'QUERY' }),\n };\n },\n\n getPool: (target: string | { serviceName: string; } | { serviceUrl: string; }): MockPool => {\n const origin = typeof target === 'string' ? target : resolveServiceUrl(target);\n return getOrCreatePool(origin).pool;\n },\n cleanup: async (): Promise<void> => {\n delete (globalThis as any).__RAPIDO_HTTP_CLIENT_MOCK_AGENT__;\n mockPools.clear();\n basePaths.clear();\n await mockAgent.close();\n mockAgent.clearCallHistory();\n },\n\n get calls(): MockCallHistoryLog[] {\n return mockAgent.getCallHistory()?.calls() ?? [];\n },\n\n clearCallHistory: (): void => mockAgent.clearCallHistory(),\n\n enableCallHistory: (): void => {\n mockAgent.enableCallHistory();\n },\n\n disableCallHistory: (): void => {\n mockAgent.disableCallHistory();\n },\n\n mockAgent,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import{RetryHandler as e}from"undici";import{env as t}from"node:process";import{createZstdDecompress as n}from"node:zlib";const r=()=>typeof n==`function`,i={timeout:1e4,headers:{"X-AF-AUTH":`ANYONE`,"X-IAF-ORIGIN-SERVICE":t.AF_SERVICE_NAME||``},keepAliveTimeout:5e3,keepAliveMaxTimeout:1e4,connections:10},a={...i,headers:{...i.headers,"Accept-Encoding":`${r()?`zstd, `:``}br, gzip`}},o=e[Object.getOwnPropertySymbols(e).find(t=>typeof Object.getOwnPropertyDescriptor(e,t)?.value==`function`&&t.toString().includes(`default retry`))],s=e=>e instanceof FormData||Object.prototype.toString.call(e)===`[object FormData]`;function c(e){if(!e.serviceUrl&&!e.serviceName)throw Error(`At least one of the settings Missing serviceUrl or serviceName`);if(e.serviceUrl)return e.serviceUrl;let n=`${e.serviceName}_SERVICE_HOST`,r=t[n];if(!r&&t.NODE_ENV!==`test`)throw Error(`Environment variable ${n} is not set`);return`http://${r}`}const l=(e,t,n)=>`[${e.toUpperCase()}] ${t??`unknown-base-url`}${n}`,u=(e,t,n)=>{if(/^https?:\/\//i.test(e)){let t=new URL(e);return{origin:t.origin,path:t.pathname+t.search+t.hash}}let r=e;if(n){let t=n.endsWith(`/`),i=e.startsWith(`/`);r=t&&i?n+e.slice(1):!t&&!i?n+`/`+e:n+e}return{origin:t,path:r}},d=(e,t)=>{let n={...e,...t};return t.headers&&(n.headers={...e.headers,...t.headers}),n};export{s as a,c,a as i,o as n,d as o,i as r,u as s,l as t};
|
|
2
|
-
//# sourceMappingURL=utils-
|
|
2
|
+
//# sourceMappingURL=utils-CN26OYva.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils-
|
|
1
|
+
{"version":3,"file":"utils-CN26OYva.js","names":["defaultSettings: Omit<RapidoHttpClientSettings, 'logger'>","defaultSettingsWithDecompress: Omit<RapidoHttpClientSettings, 'logger'>"],"sources":["../src/utils.ts"],"sourcesContent":["import { env } from 'node:process';\nimport { RetryHandler } from 'undici';\n// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-ignore createZstdDecompress is rather new, so our @types/node is unaware of it\nimport { createZstdDecompress } from 'node:zlib';\nimport type { RapidoHttpClientSettings } from '.';\n\n/** The support for zstd in Node was added during 22 & 23, so this function checks if it's exists in runtime */\nexport const isZstdSupported = (): boolean => typeof createZstdDecompress === 'function';\n\nexport const defaultSettings: Omit<RapidoHttpClientSettings, 'logger'> = {\n timeout: 10_000,\n headers: {\n 'X-AF-AUTH': 'ANYONE',\n 'X-IAF-ORIGIN-SERVICE': env.AF_SERVICE_NAME || '',\n },\n keepAliveTimeout: 5_000,\n keepAliveMaxTimeout: 10_000,\n connections: 10,\n};\n\nexport const defaultSettingsWithDecompress: Omit<RapidoHttpClientSettings, 'logger'> = {\n ...defaultSettings,\n headers: {\n ...defaultSettings.headers,\n /* c8 ignore next */\n 'Accept-Encoding': `${isZstdSupported() ? 'zstd, ' : ''}br, gzip`,\n },\n};\n\nconst retryHandlerDefaultRetryKey = Object.getOwnPropertySymbols(RetryHandler)\n .find(k => typeof Object.getOwnPropertyDescriptor(RetryHandler, k)?.value === 'function' && k.toString().includes('default retry'))!;\nexport const defaultRetryMethod = (RetryHandler as any)[retryHandlerDefaultRetryKey] as RetryHandler.RetryCallback;\n\nexport const isFormData = (obj: unknown): obj is FormData => obj instanceof FormData || Object.prototype.toString.call(obj) === '[object FormData]';\n\n/**\n * Resolves a service configuration to a base URL\n *\n * @param config - Object with either serviceName or serviceUrl\n * @returns The resolved base URL\n * @throws Error if neither serviceName nor serviceUrl is provided, or if the environment variable for serviceName is not set\n */\nexport function resolveServiceUrl(config: { serviceName?: string; serviceUrl?: string; }): string {\n if (!config.serviceUrl && !config.serviceName) {\n throw new Error('At least one of the settings Missing serviceUrl or serviceName');\n }\n\n if (config.serviceUrl) {\n return config.serviceUrl;\n }\n\n const envServiceHostName = `${config.serviceName}_SERVICE_HOST`;\n const host = env[envServiceHostName];\n if (!host && env.NODE_ENV !== 'test') {\n throw new Error(`Environment variable ${envServiceHostName} is not set`);\n }\n return `http://${host}`;\n}\n\nexport const createRequestString = (method: string, baseURL: string | undefined, url: string): string => `[${method.toUpperCase()}] ${baseURL ?? 'unknown-base-url'}${url}`;\n\nexport const parseUrlAndOrigin = (url: string, baseURL: string, basePath?: string): { origin: string; path: string; } => {\n // Check if url is a full URL (starts with http:// or https://)\n const isFullUrl = /^https?:\\/\\//i.test(url);\n\n if (isFullUrl) {\n // Parse the full URL to extract origin and path\n const urlObj = new URL(url);\n return {\n origin: urlObj.origin,\n path: urlObj.pathname + urlObj.search + urlObj.hash,\n };\n }\n\n // Use base URL and the provided path, prepending basePath if provided\n let finalPath = url;\n if (basePath) {\n // Normalize: ensure exactly one slash between basePath and url\n const baseEndsWithSlash = basePath.endsWith('/');\n const urlStartsWithSlash = url.startsWith('/');\n\n if (baseEndsWithSlash && urlStartsWithSlash) {\n // Both have slash, remove one\n finalPath = basePath + url.slice(1);\n } else if (!baseEndsWithSlash && !urlStartsWithSlash) {\n // Neither has slash, add one\n finalPath = basePath + '/' + url;\n } else {\n // One has slash, just concatenate\n finalPath = basePath + url;\n }\n }\n return {\n origin: baseURL,\n path: finalPath,\n };\n};\n\nexport const mergeConfig = <T extends Record<string, any>, U extends Record<string, any>>(\n defaults: T,\n overrides: U,\n): T & U => {\n const result = { ...defaults, ...overrides };\n\n // Special handling for headers - merge them instead of replacing\n if (overrides.headers) {\n (result as unknown as { headers: Record<string, string>; }).headers = {\n ...defaults.headers,\n ...overrides.headers,\n };\n }\n\n return result;\n};\n"],"mappings":"0HAQA,MAAa,MAAiC,OAAO,GAAyB,WAEjEA,EAA4D,CACvE,QAAS,IACT,QAAS,CACP,YAAa,SACb,uBAAwB,EAAI,iBAAmB,GAChD,CACD,iBAAkB,IAClB,oBAAqB,IACrB,YAAa,GACd,CAEYC,EAA0E,CACrF,GAAG,EACH,QAAS,CACP,GAAG,EAAgB,QAEnB,kBAAmB,GAAG,GAAiB,CAAG,SAAW,GAAG,UACzD,CACF,CAIY,EAAsB,EAFC,OAAO,sBAAsB,EAAa,CAC3E,KAAK,GAAK,OAAO,OAAO,yBAAyB,EAAc,EAAE,EAAE,OAAU,YAAc,EAAE,UAAU,CAAC,SAAS,gBAAgB,CAAC,EAGxH,EAAc,GAAkC,aAAe,UAAY,OAAO,UAAU,SAAS,KAAK,EAAI,GAAK,oBAShI,SAAgB,EAAkB,EAAgE,CAChG,GAAI,CAAC,EAAO,YAAc,CAAC,EAAO,YAChC,MAAU,MAAM,iEAAiE,CAGnF,GAAI,EAAO,WACT,OAAO,EAAO,WAGhB,IAAM,EAAqB,GAAG,EAAO,YAAY,eAC3C,EAAO,EAAI,GACjB,GAAI,CAAC,GAAQ,EAAI,WAAa,OAC5B,MAAU,MAAM,wBAAwB,EAAmB,aAAa,CAE1E,MAAO,UAAU,IAGnB,MAAa,GAAuB,EAAgB,EAA6B,IAAwB,IAAI,EAAO,aAAa,CAAC,IAAI,GAAW,qBAAqB,IAEzJ,GAAqB,EAAa,EAAiB,IAAyD,CAIvH,GAFkB,gBAAgB,KAAK,EAAI,CAE5B,CAEb,IAAM,EAAS,IAAI,IAAI,EAAI,CAC3B,MAAO,CACL,OAAQ,EAAO,OACf,KAAM,EAAO,SAAW,EAAO,OAAS,EAAO,KAChD,CAIH,IAAI,EAAY,EAChB,GAAI,EAAU,CAEZ,IAAM,EAAoB,EAAS,SAAS,IAAI,CAC1C,EAAqB,EAAI,WAAW,IAAI,CAE9C,AAQE,EARE,GAAqB,EAEX,EAAW,EAAI,MAAM,EAAE,CAC1B,CAAC,GAAqB,CAAC,EAEpB,EAAW,IAAM,EAGjB,EAAW,EAG3B,MAAO,CACL,OAAQ,EACR,KAAM,EACP,EAGU,GACX,EACA,IACU,CACV,IAAM,EAAS,CAAE,GAAG,EAAU,GAAG,EAAW,CAU5C,OAPI,EAAU,UACX,EAA2D,QAAU,CACpE,GAAG,EAAS,QACZ,GAAG,EAAU,QACd,EAGI"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
const e=require(`./index.cjs`);let t=require(`undici`),n=require(`node:process`),r=require(`node:zlib`);const i=()=>typeof r.createZstdDecompress==`function`,a={timeout:1e4,headers:{"X-AF-AUTH":`ANYONE`,"X-IAF-ORIGIN-SERVICE":n.env.AF_SERVICE_NAME||``},keepAliveTimeout:5e3,keepAliveMaxTimeout:1e4,connections:10},o={...a,headers:{...a.headers,"Accept-Encoding":`${i()?`zstd, `:``}br, gzip`}},s=Object.getOwnPropertySymbols(t.RetryHandler).find(e=>typeof Object.getOwnPropertyDescriptor(t.RetryHandler,e)?.value==`function`&&e.toString().includes(`default retry`)),c=t.RetryHandler[s],l=e=>e instanceof FormData||Object.prototype.toString.call(e)===`[object FormData]`;function u(e){if(!e.serviceUrl&&!e.serviceName)throw Error(`At least one of the settings Missing serviceUrl or serviceName`);if(e.serviceUrl)return e.serviceUrl;let t=`${e.serviceName}_SERVICE_HOST`,r=n.env[t];if(!r&&n.env.NODE_ENV!==`test`)throw Error(`Environment variable ${t} is not set`);return`http://${r}`}const d=(e,t,n)=>`[${e.toUpperCase()}] ${t??`unknown-base-url`}${n}`,f=(e,t,n)=>{if(/^https?:\/\//i.test(e)){let t=new URL(e);return{origin:t.origin,path:t.pathname+t.search+t.hash}}let r=e;if(n){let t=n.endsWith(`/`),i=e.startsWith(`/`);r=t&&i?n+e.slice(1):!t&&!i?n+`/`+e:n+e}return{origin:t,path:r}},p=(e,t)=>{let n={...e,...t};return t.headers&&(n.headers={...e.headers,...t.headers}),n};Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return d}});
|
|
2
|
-
//# sourceMappingURL=utils-
|
|
2
|
+
//# sourceMappingURL=utils-Dh5SqGdK.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils-
|
|
1
|
+
{"version":3,"file":"utils-Dh5SqGdK.cjs","names":["createZstdDecompress","defaultSettings: Omit<RapidoHttpClientSettings, 'logger'>","env","defaultSettingsWithDecompress: Omit<RapidoHttpClientSettings, 'logger'>","RetryHandler"],"sources":["../src/utils.ts"],"sourcesContent":["import { env } from 'node:process';\nimport { RetryHandler } from 'undici';\n// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-ignore createZstdDecompress is rather new, so our @types/node is unaware of it\nimport { createZstdDecompress } from 'node:zlib';\nimport type { RapidoHttpClientSettings } from '.';\n\n/** The support for zstd in Node was added during 22 & 23, so this function checks if it's exists in runtime */\nexport const isZstdSupported = (): boolean => typeof createZstdDecompress === 'function';\n\nexport const defaultSettings: Omit<RapidoHttpClientSettings, 'logger'> = {\n timeout: 10_000,\n headers: {\n 'X-AF-AUTH': 'ANYONE',\n 'X-IAF-ORIGIN-SERVICE': env.AF_SERVICE_NAME || '',\n },\n keepAliveTimeout: 5_000,\n keepAliveMaxTimeout: 10_000,\n connections: 10,\n};\n\nexport const defaultSettingsWithDecompress: Omit<RapidoHttpClientSettings, 'logger'> = {\n ...defaultSettings,\n headers: {\n ...defaultSettings.headers,\n /* c8 ignore next */\n 'Accept-Encoding': `${isZstdSupported() ? 'zstd, ' : ''}br, gzip`,\n },\n};\n\nconst retryHandlerDefaultRetryKey = Object.getOwnPropertySymbols(RetryHandler)\n .find(k => typeof Object.getOwnPropertyDescriptor(RetryHandler, k)?.value === 'function' && k.toString().includes('default retry'))!;\nexport const defaultRetryMethod = (RetryHandler as any)[retryHandlerDefaultRetryKey] as RetryHandler.RetryCallback;\n\nexport const isFormData = (obj: unknown): obj is FormData => obj instanceof FormData || Object.prototype.toString.call(obj) === '[object FormData]';\n\n/**\n * Resolves a service configuration to a base URL\n *\n * @param config - Object with either serviceName or serviceUrl\n * @returns The resolved base URL\n * @throws Error if neither serviceName nor serviceUrl is provided, or if the environment variable for serviceName is not set\n */\nexport function resolveServiceUrl(config: { serviceName?: string; serviceUrl?: string; }): string {\n if (!config.serviceUrl && !config.serviceName) {\n throw new Error('At least one of the settings Missing serviceUrl or serviceName');\n }\n\n if (config.serviceUrl) {\n return config.serviceUrl;\n }\n\n const envServiceHostName = `${config.serviceName}_SERVICE_HOST`;\n const host = env[envServiceHostName];\n if (!host && env.NODE_ENV !== 'test') {\n throw new Error(`Environment variable ${envServiceHostName} is not set`);\n }\n return `http://${host}`;\n}\n\nexport const createRequestString = (method: string, baseURL: string | undefined, url: string): string => `[${method.toUpperCase()}] ${baseURL ?? 'unknown-base-url'}${url}`;\n\nexport const parseUrlAndOrigin = (url: string, baseURL: string, basePath?: string): { origin: string; path: string; } => {\n // Check if url is a full URL (starts with http:// or https://)\n const isFullUrl = /^https?:\\/\\//i.test(url);\n\n if (isFullUrl) {\n // Parse the full URL to extract origin and path\n const urlObj = new URL(url);\n return {\n origin: urlObj.origin,\n path: urlObj.pathname + urlObj.search + urlObj.hash,\n };\n }\n\n // Use base URL and the provided path, prepending basePath if provided\n let finalPath = url;\n if (basePath) {\n // Normalize: ensure exactly one slash between basePath and url\n const baseEndsWithSlash = basePath.endsWith('/');\n const urlStartsWithSlash = url.startsWith('/');\n\n if (baseEndsWithSlash && urlStartsWithSlash) {\n // Both have slash, remove one\n finalPath = basePath + url.slice(1);\n } else if (!baseEndsWithSlash && !urlStartsWithSlash) {\n // Neither has slash, add one\n finalPath = basePath + '/' + url;\n } else {\n // One has slash, just concatenate\n finalPath = basePath + url;\n }\n }\n return {\n origin: baseURL,\n path: finalPath,\n };\n};\n\nexport const mergeConfig = <T extends Record<string, any>, U extends Record<string, any>>(\n defaults: T,\n overrides: U,\n): T & U => {\n const result = { ...defaults, ...overrides };\n\n // Special handling for headers - merge them instead of replacing\n if (overrides.headers) {\n (result as unknown as { headers: Record<string, string>; }).headers = {\n ...defaults.headers,\n ...overrides.headers,\n };\n }\n\n return result;\n};\n"],"mappings":"wGAQA,MAAa,MAAiC,OAAOA,EAAAA,sBAAyB,WAEjEC,EAA4D,CACvE,QAAS,IACT,QAAS,CACP,YAAa,SACb,uBAAwBC,EAAAA,IAAI,iBAAmB,GAChD,CACD,iBAAkB,IAClB,oBAAqB,IACrB,YAAa,GACd,CAEYC,EAA0E,CACrF,GAAG,EACH,QAAS,CACP,GAAG,EAAgB,QAEnB,kBAAmB,GAAG,GAAiB,CAAG,SAAW,GAAG,UACzD,CACF,CAEK,EAA8B,OAAO,sBAAsBC,EAAAA,aAAa,CAC3E,KAAK,GAAK,OAAO,OAAO,yBAAyBA,EAAAA,aAAc,EAAE,EAAE,OAAU,YAAc,EAAE,UAAU,CAAC,SAAS,gBAAgB,CAAC,CACxH,EAAsBA,EAAAA,aAAqB,GAE3C,EAAc,GAAkC,aAAe,UAAY,OAAO,UAAU,SAAS,KAAK,EAAI,GAAK,oBAShI,SAAgB,EAAkB,EAAgE,CAChG,GAAI,CAAC,EAAO,YAAc,CAAC,EAAO,YAChC,MAAU,MAAM,iEAAiE,CAGnF,GAAI,EAAO,WACT,OAAO,EAAO,WAGhB,IAAM,EAAqB,GAAG,EAAO,YAAY,eAC3C,EAAOF,EAAAA,IAAI,GACjB,GAAI,CAAC,GAAQA,EAAAA,IAAI,WAAa,OAC5B,MAAU,MAAM,wBAAwB,EAAmB,aAAa,CAE1E,MAAO,UAAU,IAGnB,MAAa,GAAuB,EAAgB,EAA6B,IAAwB,IAAI,EAAO,aAAa,CAAC,IAAI,GAAW,qBAAqB,IAEzJ,GAAqB,EAAa,EAAiB,IAAyD,CAIvH,GAFkB,gBAAgB,KAAK,EAAI,CAE5B,CAEb,IAAM,EAAS,IAAI,IAAI,EAAI,CAC3B,MAAO,CACL,OAAQ,EAAO,OACf,KAAM,EAAO,SAAW,EAAO,OAAS,EAAO,KAChD,CAIH,IAAI,EAAY,EAChB,GAAI,EAAU,CAEZ,IAAM,EAAoB,EAAS,SAAS,IAAI,CAC1C,EAAqB,EAAI,WAAW,IAAI,CAE9C,AAQE,EARE,GAAqB,EAEX,EAAW,EAAI,MAAM,EAAE,CAC1B,CAAC,GAAqB,CAAC,EAEpB,EAAW,IAAM,EAGjB,EAAW,EAG3B,MAAO,CACL,OAAQ,EACR,KAAM,EACP,EAGU,GACX,EACA,IACU,CACV,IAAM,EAAS,CAAE,GAAG,EAAU,GAAG,EAAW,CAU5C,OAPI,EAAU,UACX,EAA2D,QAAU,CACpE,GAAG,EAAS,QACZ,GAAG,EAAU,QACd,EAGI"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autofleet/rapido-http-client",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Modern undici-based HTTP client with logging, retries, and global configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"@autofleet/logger": ">=4.2.0",
|
|
52
52
|
"@autofleet/outbreak": ">=2",
|
|
53
|
-
"zod": ">=4"
|
|
53
|
+
"zod": ">=4.2"
|
|
54
54
|
},
|
|
55
55
|
"peerDependenciesMeta": {
|
|
56
56
|
"zod": {
|
|
@@ -60,9 +60,10 @@
|
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@types/node": "^20.0.0",
|
|
62
62
|
"nock": "^14.0.1",
|
|
63
|
-
"
|
|
64
|
-
"@autofleet/
|
|
65
|
-
"@autofleet/
|
|
63
|
+
"zod": "^4.2.1",
|
|
64
|
+
"@autofleet/logger": "^4.2.37",
|
|
65
|
+
"@autofleet/network": "^1.9.3",
|
|
66
|
+
"@autofleet/outbreak": "^2.5.9"
|
|
66
67
|
},
|
|
67
68
|
"scripts": {
|
|
68
69
|
"test": "vitest",
|