From c0c56c1cf3319911ce4ca57721cb3ba47bf1ad76 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 3 Apr 2026 14:07:56 +0100 Subject: [PATCH] feat: remove `extend` dependency --- auth/authenticators/basic-authenticator.ts | 3 +-- .../bearer-token-authenticator.ts | 3 +-- ...n-request-based-authenticator-immutable.ts | 3 +-- auth/token-managers/cp4d-token-manager.ts | 3 +-- .../iam-request-based-token-manager.ts | 3 +-- auth/token-managers/mcsp-token-manager.ts | 3 +-- auth/token-managers/mcspv2-token-manager.ts | 3 +-- lib/base-service.ts | 11 ++++------ lib/cookie-support.ts | 4 +--- lib/request-wrapper.ts | 21 +++++++++---------- 10 files changed, 22 insertions(+), 35 deletions(-) diff --git a/auth/authenticators/basic-authenticator.ts b/auth/authenticators/basic-authenticator.ts index aef1c20ee..357ece785 100644 --- a/auth/authenticators/basic-authenticator.ts +++ b/auth/authenticators/basic-authenticator.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import extend from 'extend'; import { computeBasicAuthHeader, validateInput } from '../utils/helpers'; import { Authenticator } from './authenticator'; import { AuthenticateOptions } from './authenticator-interface'; @@ -72,7 +71,7 @@ export class BasicAuthenticator extends Authenticator { */ public authenticate(requestOptions: AuthenticateOptions): Promise { return new Promise((resolve) => { - requestOptions.headers = extend(true, {}, requestOptions.headers, this.authHeader); + requestOptions.headers = { ...requestOptions.headers, ...this.authHeader }; logger.debug(`Authenticated outbound request (type=${this.authenticationType()})`); resolve(); }); diff --git a/auth/authenticators/bearer-token-authenticator.ts b/auth/authenticators/bearer-token-authenticator.ts index 4fe2faa95..9f4ec6bfa 100644 --- a/auth/authenticators/bearer-token-authenticator.ts +++ b/auth/authenticators/bearer-token-authenticator.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import extend from 'extend'; import { validateInput } from '../utils/helpers'; import { Authenticator } from './authenticator'; import { AuthenticateOptions } from './authenticator-interface'; @@ -76,7 +75,7 @@ export class BearerTokenAuthenticator extends Authenticator { public authenticate(requestOptions: AuthenticateOptions): Promise { return new Promise((resolve) => { const authHeader = { Authorization: `Bearer ${this.bearerToken}` }; - requestOptions.headers = extend(true, {}, requestOptions.headers, authHeader); + requestOptions.headers = { ...requestOptions.headers, ...authHeader }; logger.debug(`Authenticated outbound request (type=${this.authenticationType()})`); resolve(); }); diff --git a/auth/authenticators/token-request-based-authenticator-immutable.ts b/auth/authenticators/token-request-based-authenticator-immutable.ts index 763148f83..7204ad503 100644 --- a/auth/authenticators/token-request-based-authenticator-immutable.ts +++ b/auth/authenticators/token-request-based-authenticator-immutable.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import extend from 'extend'; import { OutgoingHttpHeaders } from 'http'; import { JwtTokenManager } from '../token-managers/jwt-token-manager'; import { Authenticator } from './authenticator'; @@ -89,7 +88,7 @@ export class TokenRequestBasedAuthenticatorImmutable extends Authenticator { public authenticate(requestOptions: AuthenticateOptions): Promise { return this.tokenManager.getToken().then((token) => { const authHeader = { Authorization: `Bearer ${token}` }; - requestOptions.headers = extend(true, {}, requestOptions.headers, authHeader); + requestOptions.headers = { ...requestOptions.headers, ...authHeader }; logger.debug(`Authenticated outbound request (type=${this.authenticationType()})`); }); } diff --git a/auth/token-managers/cp4d-token-manager.ts b/auth/token-managers/cp4d-token-manager.ts index 5f90bdd2c..9d7db0ed6 100644 --- a/auth/token-managers/cp4d-token-manager.ts +++ b/auth/token-managers/cp4d-token-manager.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import extend from 'extend'; import { validateInput } from '../utils/helpers'; import { buildUserAgent } from '../../lib/build-user-agent'; import { JwtTokenManager, JwtTokenManagerOptions } from './jwt-token-manager'; @@ -118,7 +117,7 @@ export class Cp4dTokenManager extends JwtTokenManager { api_key: this.apikey, }, method: 'POST', - headers: extend(true, {}, this.headers, requiredHeaders), + headers: { ...this.headers, ...requiredHeaders }, rejectUnauthorized: !this.disableSslVerification, }, }; diff --git a/auth/token-managers/iam-request-based-token-manager.ts b/auth/token-managers/iam-request-based-token-manager.ts index 8dc4c24bc..588748924 100644 --- a/auth/token-managers/iam-request-based-token-manager.ts +++ b/auth/token-managers/iam-request-based-token-manager.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import extend from 'extend'; import { OutgoingHttpHeaders } from 'http'; import logger from '../../lib/logger'; import { computeBasicAuthHeader, getCurrentTime, onlyOne, removeSuffix } from '../utils/helpers'; @@ -165,7 +164,7 @@ export class IamRequestBasedTokenManager extends JwtTokenManager { options: { url: this.url + OPERATION_PATH, method: 'POST', - headers: extend(true, {}, this.headers, requiredHeaders), + headers: { ...this.headers, ...requiredHeaders }, form: this.formData, rejectUnauthorized: !this.disableSslVerification, }, diff --git a/auth/token-managers/mcsp-token-manager.ts b/auth/token-managers/mcsp-token-manager.ts index 096fe9709..fa7ec3cbb 100644 --- a/auth/token-managers/mcsp-token-manager.ts +++ b/auth/token-managers/mcsp-token-manager.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import extend from 'extend'; import { validateInput } from '../utils/helpers'; import { buildUserAgent } from '../../lib/build-user-agent'; import { JwtTokenManager, JwtTokenManagerOptions } from './jwt-token-manager'; @@ -96,7 +95,7 @@ export class McspTokenManager extends JwtTokenManager { apikey: this.apikey, }, method: 'POST', - headers: extend(true, {}, this.headers, requiredHeaders), + headers: { ...this.headers, ...requiredHeaders }, rejectUnauthorized: !this.disableSslVerification, }, }; diff --git a/auth/token-managers/mcspv2-token-manager.ts b/auth/token-managers/mcspv2-token-manager.ts index 8209e0cbd..421285bb3 100644 --- a/auth/token-managers/mcspv2-token-manager.ts +++ b/auth/token-managers/mcspv2-token-manager.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import extend from 'extend'; import { validateInput } from '../utils/helpers'; import { buildUserAgent } from '../../lib/build-user-agent'; import { JwtTokenManager, JwtTokenManagerOptions } from './jwt-token-manager'; @@ -174,7 +173,7 @@ export class McspV2TokenManager extends JwtTokenManager { 'User-Agent': this.userAgent, }; - const requestHeaders = extend(true, {}, this.headers, requiredHeaders); + const requestHeaders = { ...this.headers, ...requiredHeaders }; // The keys used here must match the path parameter references in PATH_TEMPLATE above. const pathParams = { diff --git a/lib/base-service.ts b/lib/base-service.ts index 515ce2e71..2f0f1883a 100644 --- a/lib/base-service.ts +++ b/lib/base-service.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import extend from 'extend'; import type { CookieJar } from 'tough-cookie'; import { OutgoingHttpHeaders } from 'http'; import { AuthenticatorInterface, checkCredentials, readExternalSources } from '../auth'; @@ -284,12 +283,10 @@ export class BaseService { const userAgent = { 'User-Agent': this.defaultUserAgent, }; - parameters.defaultOptions.headers = extend( - true, - {}, - userAgent, - parameters.defaultOptions.headers - ); + parameters.defaultOptions.headers = { + ...userAgent, + ...parameters.defaultOptions.headers, + }; return this.authenticator.authenticate(parameters.defaultOptions).then(() => // resolve() handles rejection as well, so resolving the result of sendRequest should allow for proper handling later diff --git a/lib/cookie-support.ts b/lib/cookie-support.ts index 4c9343012..3a3728f73 100644 --- a/lib/cookie-support.ts +++ b/lib/cookie-support.ts @@ -15,7 +15,6 @@ */ import { Axios, AxiosResponse, InternalAxiosRequestConfig, isAxiosError } from 'axios'; -import extend from 'extend'; import { Cookie, CookieJar } from 'tough-cookie'; import logger from './logger'; @@ -34,8 +33,7 @@ const internalCreateCookieInterceptor = (cookieJar: CookieJar) => { const cookieHeaderValue = await cookieJar.getCookieString(config.url); if (cookieHeaderValue) { logger.debug('CookieInterceptor: setting cookie header'); - const cookieHeader = { cookie: cookieHeaderValue }; - config.headers = extend(true, {}, config.headers, cookieHeader); + config.headers.set('cookie', cookieHeaderValue); } else { logger.debug(`CookieInterceptor: no cookies for: ${config.url}`); } diff --git a/lib/request-wrapper.ts b/lib/request-wrapper.ts index 7c4682b98..c7d1fc705 100644 --- a/lib/request-wrapper.ts +++ b/lib/request-wrapper.ts @@ -24,7 +24,6 @@ import axios, { InternalAxiosRequestConfig, } from 'axios'; import * as rax from 'retry-axios'; -import extend from 'extend'; import FormData from 'form-data'; import { OutgoingHttpHeaders } from 'http'; import { Agent } from 'https'; @@ -70,18 +69,16 @@ export class RequestWrapper { private raxConfig: rax.RetryConfig; constructor(axiosOptions?) { - axiosOptions = axiosOptions || {}; + axiosOptions ??= {}; this.compressRequestData = Boolean(axiosOptions.enableGzipCompression); - // override a couple axios defaults + // override a couple axios defaults then merge axios config into default const axiosConfig: AxiosRequestConfig = { maxContentLength: -1, maxBodyLength: Infinity, + ...axiosOptions, }; - // merge axios config into default - extend(true, axiosConfig, axiosOptions); - // if the user explicitly sets `disableSslVerification` to true, // `rejectUnauthorized` must be set to false in the https agent if (axiosOptions.disableSslVerification === true) { @@ -248,9 +245,11 @@ export class RequestWrapper { * @throws Error */ public async sendRequest(parameters): Promise { - const options = extend(true, {}, parameters.defaultOptions, parameters.options); - const { path, body, form, formData, qs, method, serviceUrl, axiosOptions } = options; - let { headers, url } = options; + const options = { ...parameters.defaultOptions, ...parameters.options }; + let headers = { ...parameters.defaultOptions?.headers, ...parameters.options?.headers }; + const qs = { ...parameters.defaultOptions?.qs, ...parameters.options?.qs }; + const { path, body, form, formData, method, serviceUrl, axiosOptions } = options; + let { url } = options; const multipartForm = new FormData(); @@ -313,11 +312,11 @@ export class RequestWrapper { if (formData) { data = multipartForm; // form-data generates headers that MUST be included or the request will fail - headers = extend(true, {}, headers, multipartForm.getHeaders()); + headers = { ...headers, ...multipartForm.getHeaders() }; } // accept gzip encoded responses if Accept-Encoding is not already set - headers['Accept-Encoding'] = headers['Accept-Encoding'] || 'gzip'; + headers['Accept-Encoding'] ||= 'gzip'; // compress request body data if enabled if (this.compressRequestData) {