From 3cbda50f97038bde8e94d2dab4d2e3c64e9f2571 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 16:12:18 +0000 Subject: [PATCH] refactor(http-agent): improve type safety and remove @ts-expect-error This commit refactors the `src/lib/http-agent.ts` module to improve type safety and remove all instances of `@ts-expect-error`. The following changes were made: - Introduced a custom type definition for `https-proxy-agent` to properly type the `HttpsProxyAgentWithCA` class. - Added a type guard to safely handle errors in `tryGetAgent`, removing the need for `@ts-expect-error`. - Corrected the return type of `tryGetAgent` to accurately reflect all possible return values. - Refactored the `getAgent` function to properly handle the return type of `tryGetAgent`, eliminating another `@ts-expect-error`. --- package-lock.json | 42 +++++++++++----------- src/lib/http-agent.ts | 58 +++++++++++++++++------------- types/https-proxy-agent/index.d.ts | 16 +++++++++ 3 files changed, 70 insertions(+), 46 deletions(-) create mode 100644 types/https-proxy-agent/index.d.ts diff --git a/package-lock.json b/package-lock.json index 28caec0968a..55a6636c810 100644 --- a/package-lock.json +++ b/package-lock.json @@ -208,7 +208,6 @@ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", "dev": true, - "peer": true, "engines": { "node": ">=6.9.0" } @@ -249,7 +248,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "peer": true, "bin": { "semver": "bin/semver.js" } @@ -287,7 +285,6 @@ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, - "peer": true, "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", @@ -304,7 +301,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "peer": true, "bin": { "semver": "bin/semver.js" } @@ -336,7 +332,6 @@ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", "dev": true, - "peer": true, "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", @@ -389,7 +384,6 @@ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dev": true, - "peer": true, "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" @@ -2050,7 +2044,6 @@ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, - "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -3731,6 +3724,7 @@ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.5.tgz", "integrity": "sha512-t54CUOsFMappY1Jbzb7fetWeO0n6K0k/4+/ZpkS+3Joz8I4VcvY9OiEBFRYISqaI2fq5sCiPtAjRDOzVYG8m+Q==", "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.2", @@ -3897,6 +3891,7 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.8.0.tgz", "integrity": "sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==", + "peer": true, "engines": { "node": ">=8.0.0" } @@ -4913,6 +4908,7 @@ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", "dev": true, + "peer": true, "dependencies": { "@types/ms": "*" } @@ -4955,6 +4951,7 @@ "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -5090,6 +5087,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.11.tgz", "integrity": "sha512-Gd33J2XIrXurb+eT2ktze3rJAfAp9ZNjlBdh4SVgyrKEOADwCbdUDaK7QgJno8Ue4kcajscsKqu6n8OBG3hhCQ==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -5406,6 +5404,7 @@ "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.45.0", "@typescript-eslint/types": "8.45.0", @@ -6746,6 +6745,7 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6793,6 +6793,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -7227,7 +7228,6 @@ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.7.tgz", "integrity": "sha512-bxxN2M3a4d1CRoQC//IqsR5XrLh0IJ8TCv2x6Y9N0nckNz/rTjZB3//GGscZziZOxmjP55rzxg/ze7usFI9FqQ==", "dev": true, - "peer": true, "bin": { "baseline-browser-mapping": "dist/cli.js" } @@ -7808,8 +7808,7 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ], - "peer": true + ] }, "node_modules/caseless": { "version": "0.12.0", @@ -9284,8 +9283,7 @@ "version": "1.5.223", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.223.tgz", "integrity": "sha512-qKm55ic6nbEmagFlTFczML33rF90aU+WtrJ9MdTCThrcvDNdUHN4p6QfVN78U06ZmguqXIyMPyYhw2TrbDUwPQ==", - "dev": true, - "peer": true + "dev": true }, "node_modules/emoji-regex": { "version": "10.5.0", @@ -9577,6 +9575,7 @@ "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -10943,7 +10942,6 @@ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, - "peer": true, "engines": { "node": ">=6.9.0" } @@ -12759,7 +12757,6 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "peer": true, "bin": { "json5": "lib/cli.js" }, @@ -13404,7 +13401,6 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, - "peer": true, "dependencies": { "yallist": "^3.0.2" } @@ -14010,8 +14006,7 @@ "version": "2.0.21", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", - "dev": true, - "peer": true + "dev": true }, "node_modules/node-source-walk": { "version": "7.0.1", @@ -15032,6 +15027,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -15745,6 +15741,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.2.tgz", "integrity": "sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA==", "devOptional": true, + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -17124,7 +17121,8 @@ "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "peer": true }, "node_modules/tsx": { "version": "4.20.6", @@ -17132,6 +17130,7 @@ "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" @@ -17212,6 +17211,7 @@ "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -17523,7 +17523,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" @@ -17990,6 +17989,7 @@ "integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -18086,6 +18086,7 @@ "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, + "peer": true, "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", @@ -18677,8 +18678,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "peer": true + "dev": true }, "node_modules/yaml": { "version": "2.8.1", diff --git a/src/lib/http-agent.ts b/src/lib/http-agent.ts index d9f44df9b65..d39e51d5b61 100644 --- a/src/lib/http-agent.ts +++ b/src/lib/http-agent.ts @@ -1,26 +1,24 @@ import { readFile } from 'fs/promises' +import { ClientRequest, RequestOptions } from 'http' -import { HttpsProxyAgent } from 'https-proxy-agent' +import { HttpsProxyAgent, HttpsProxyAgentOptions } from 'https-proxy-agent' import { NETLIFYDEVERR, NETLIFYDEVWARN, exit, log } from '../utils/command-helpers.js' import { waitPort } from './wait-port.js' // https://github.com/TooTallNate/node-https-proxy-agent/issues/89 // Maybe replace with https://github.com/delvedor/hpagent -// @ts-expect-error TS(2507) FIXME: Type 'typeof createHttpsProxyAgent' is not a const... Remove this comment to see the full error message class HttpsProxyAgentWithCA extends HttpsProxyAgent { - // @ts-expect-error TS(7006) FIXME: Parameter 'opts' implicitly has an 'any' type. - constructor(opts) { + ca: Buffer | undefined + + constructor(opts: HttpsProxyAgentOptions) { super(opts) - // @ts-expect-error TS(2339) FIXME: Property 'ca' does not exist on type 'HttpsProxyAg... Remove this comment to see the full error message this.ca = opts.ca } - // @ts-expect-error TS(7006) FIXME: Parameter 'req' implicitly has an 'any' type. - callback(req, opts) { + callback(req: ClientRequest, opts: RequestOptions) { return super.callback(req, { ...opts, - // @ts-expect-error TS(2339) FIXME: Property 'ca' does not exist on type 'HttpsProxyAg... Remove this comment to see the full error message ...(this.ca && { ca: this.ca }), }) } @@ -31,21 +29,27 @@ const DEFAULT_HTTPS_PORT = 443 // 50 seconds const AGENT_PORT_TIMEOUT = 50_000 +const isError = (error: unknown): error is Error => error instanceof Error + export const tryGetAgent = async ({ certificateFile, httpProxy, }: { - httpProxy?: string | undefined - certificateFile?: string | undefined + httpProxy?: string + certificateFile?: string }): Promise< | { - error?: string | undefined - warning?: string | undefined - message?: string | undefined + error?: string + warning?: string + message?: string + agent?: undefined } | { agent: HttpsProxyAgentWithCA - response: unknown + response?: unknown + error?: undefined + warning?: string + message?: string } > => { if (!httpProxy) { @@ -73,8 +77,7 @@ export const tryGetAgent = async ({ ) } catch (error) { // unknown error - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - return { error: `${httpProxy} is not available.`, message: error.message } + return { error: `${httpProxy} is not available.`, message: isError(error) ? error.message : String(error) } } if (!port.open) { @@ -82,19 +85,21 @@ export const tryGetAgent = async ({ return { error: `Could not connect to '${httpProxy}'` } } - let response = {} + let response: { warning?: string; message?: string } = {} let certificate if (certificateFile) { try { certificate = await readFile(certificateFile) } catch (error) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - response = { warning: `Could not read certificate file '${certificateFile}'.`, message: error.message } + response = { + warning: `Could not read certificate file '${certificateFile}'.`, + message: isError(error) ? error.message : String(error), + } } } - const opts = { + const opts: HttpsProxyAgentOptions = { port: proxyUrl.port, host: proxyUrl.host, hostname: proxyUrl.hostname, @@ -103,13 +108,16 @@ export const tryGetAgent = async ({ } const agent = new HttpsProxyAgentWithCA(opts) - response = { ...response, agent } - return response + return { ...response, agent } } -// @ts-expect-error TS(7031) FIXME: Binding element 'certificateFile' implicitly has a... Remove this comment to see the full error message -export const getAgent = async ({ certificateFile, httpProxy }) => { - // @ts-expect-error TS(2339) FIXME: Property 'agent' does not exist on type '{ error?:... Remove this comment to see the full error message +export const getAgent = async ({ + certificateFile, + httpProxy, +}: { + httpProxy?: string + certificateFile?: string +}) => { const { agent, error, message, warning } = await tryGetAgent({ httpProxy, certificateFile }) if (error) { log(NETLIFYDEVERR, error, message || '') diff --git a/types/https-proxy-agent/index.d.ts b/types/https-proxy-agent/index.d.ts new file mode 100644 index 00000000000..31abadb0ed9 --- /dev/null +++ b/types/https-proxy-agent/index.d.ts @@ -0,0 +1,16 @@ +declare module 'https-proxy-agent' { + import { Agent } from 'http'; + import { URL } from 'url'; + + export class HttpsProxyAgent extends Agent { + constructor(opts: HttpsProxyAgentOptions | string | URL); + } + + export interface HttpsProxyAgentOptions { + host: string; + port: string; + protocol: string; + ca?: Buffer; + [key: string]: any; + } +}