From 2f4aac006163fb9f244badb1ebf3dfaccc6b3187 Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Wed, 17 Jan 2024 12:49:45 +0100 Subject: [PATCH 01/33] feat: allow scoped client certificates --- network/agent/agent.ts | 27 +++++++++++++++++++++------ network/proxy-agent/proxy-agent.ts | 9 +++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/network/agent/agent.ts b/network/agent/agent.ts index e30fc7d..ac55fbc 100644 --- a/network/agent/agent.ts +++ b/network/agent/agent.ts @@ -13,26 +13,41 @@ export type AgentOptions = ProxyAgentOptions & { noProxy?: boolean | string } +function uriToHost (uri: string) { + const parsedUri = new URL(uri) + const port = parsedUri.port + const hostname = parsedUri.hostname + let url = `//${hostname}` + if (port) { + url += `:${port}` + } + return url + '/' +} + export function getAgent (uri: string, opts: AgentOptions) { if ((opts.httpProxy || opts.httpsProxy) && !checkNoProxy(uri, opts)) { const proxyAgent = getProxyAgent(uri, opts) if (proxyAgent) return proxyAgent } + return getNonProxyAgent(uri, opts) } function getNonProxyAgent (uri: string, opts: AgentOptions) { const parsedUri = new URL(uri) + const host = uriToHost(uri) const isHttps = parsedUri.protocol === 'https:' + const clientCertificates = opts.clientCertificates?.[host] ?? null + /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ const key = [ `https:${isHttps.toString()}`, `local-address:${opts.localAddress ?? '>no-local-address<'}`, `strict-ssl:${isHttps ? Boolean(opts.strictSsl).toString() : '>no-strict-ssl<'}`, - `ca:${(isHttps && opts.ca?.toString()) || '>no-ca<'}`, - `cert:${(isHttps && opts.cert?.toString()) || '>no-cert<'}`, - `key:${(isHttps && opts.key) || '>no-key<'}`, + `ca:${(isHttps && clientCertificates?.ca || opts.ca?.toString()) || '>no-ca<'}`, + `cert:${(isHttps && clientCertificates?.cert || opts.cert?.toString()) || '>no-cert<'}`, + `key:${(isHttps && clientCertificates?.key || opts.key) || '>no-key<'}`, ].join(':') /* eslint-enable @typescript-eslint/prefer-nullish-coalescing */ @@ -55,9 +70,9 @@ function getNonProxyAgent (uri: string, opts: AgentOptions) { // https://github.com/nodejs/node/blob/350a95b89faab526de852d417bbb8a3ac823c325/lib/_http_agent.js#L254 const agent = isHttps ? new HttpsAgent({ - ca: opts.ca, - cert: opts.cert, - key: opts.key, + ca: clientCertificates?.ca || opts.ca, + cert: clientCertificates?.cert || opts.cert, + key: clientCertificates?.key || opts.key, localAddress: opts.localAddress, maxSockets: opts.maxSockets ?? DEFAULT_MAX_SOCKETS, rejectUnauthorized: opts.strictSsl, diff --git a/network/proxy-agent/proxy-agent.ts b/network/proxy-agent/proxy-agent.ts index b54d849..a966e0f 100644 --- a/network/proxy-agent/proxy-agent.ts +++ b/network/proxy-agent/proxy-agent.ts @@ -19,6 +19,13 @@ export interface ProxyAgentOptions { noProxy?: boolean | string strictSsl?: boolean timeout?: number + clientCertificates?: { + [registryUrl: string]: { + cert: string, + key: string, + ca?: string, + }, + } } export function getProxyAgent (uri: string, opts: ProxyAgentOptions) { @@ -148,10 +155,12 @@ class PatchedHttpsProxyAgent extends HttpsProxyAgent { constructor (opts: any) { super(opts) + // @ts-expect-error this[extraOpts] = opts } callback (req: any, opts: any) { + // @ts-expect-error return super.callback(req, { ...this[extraOpts], ...opts }) } } From 8a91a3887de8623189f0494a9d0add07d3b5a205 Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Wed, 17 Jan 2024 12:52:52 +0100 Subject: [PATCH 02/33] style: remove new line and use template literal --- network/agent/agent.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/network/agent/agent.ts b/network/agent/agent.ts index ac55fbc..32d431b 100644 --- a/network/agent/agent.ts +++ b/network/agent/agent.ts @@ -21,7 +21,7 @@ function uriToHost (uri: string) { if (port) { url += `:${port}` } - return url + '/' + return `${url}/` } export function getAgent (uri: string, opts: AgentOptions) { @@ -29,7 +29,6 @@ export function getAgent (uri: string, opts: AgentOptions) { const proxyAgent = getProxyAgent(uri, opts) if (proxyAgent) return proxyAgent } - return getNonProxyAgent(uri, opts) } From f05a1053aa4ddcc420a14a1c19d8c405f5f43582 Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Wed, 17 Jan 2024 13:01:15 +0100 Subject: [PATCH 03/33] fix: use `node-fetch` latest cjs version --- pnpm-lock.yaml | 29 ++--------------------------- workspace.jsonc | 2 +- 2 files changed, 3 insertions(+), 28 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0d9ddb6..08db0a2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -58,8 +58,8 @@ importers: specifier: 4.0.1 version: registry.npmjs.org/@types/string.prototype.matchall@4.0.1 node-fetch: - specifier: 3.0.0-beta.9 - version: 3.0.0-beta.9 + specifier: 2.6.7 + version: 2.6.7 proxy: specifier: 1.0.2 version: 1.0.2 @@ -824,11 +824,6 @@ packages: assert-plus: 1.0.0 dev: true - /data-uri-to-buffer@3.0.1: - resolution: {integrity: sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==} - engines: {node: '>= 6'} - dev: true - /data-urls@1.1.0: resolution: {integrity: sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==} dependencies: @@ -1215,16 +1210,6 @@ packages: reusify: 1.0.4 dev: true - /fetch-blob@2.1.2: - resolution: {integrity: sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow==} - engines: {node: ^10.17.0 || >=12.3.0} - peerDependencies: - domexception: '*' - peerDependenciesMeta: - domexception: - optional: true - dev: true - /fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -2057,16 +2042,6 @@ packages: whatwg-url: 5.0.0 dev: true - /node-fetch@3.0.0-beta.9: - resolution: {integrity: sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg==} - engines: {node: ^10.17 || >=12.3} - dependencies: - data-uri-to-buffer: 3.0.1 - fetch-blob: 2.1.2 - transitivePeerDependencies: - - domexception - dev: true - /normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: diff --git a/workspace.jsonc b/workspace.jsonc index 48d42af..11b875f 100644 --- a/workspace.jsonc +++ b/workspace.jsonc @@ -49,7 +49,7 @@ "http-proxy-agent": "5.0.0", "https-proxy-agent": "5.0.1", "lru-cache": "7.10.1", - "node-fetch": "3.0.0-beta.9", + "node-fetch": "2.6.7", "proxy": "1.0.2", "safe-execa": "0.1.1", "socks-proxy-agent": "6.1.1", From eff7e9a475b4115219f7f5db39383dd8c8316129 Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Wed, 17 Jan 2024 13:02:28 +0100 Subject: [PATCH 04/33] test: fix self-signed certificate regex --- network/proxy-agent/proxy-agent.strict-ssl.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/proxy-agent/proxy-agent.strict-ssl.spec.ts b/network/proxy-agent/proxy-agent.strict-ssl.spec.ts index 37cbe8a..6225798 100644 --- a/network/proxy-agent/proxy-agent.strict-ssl.spec.ts +++ b/network/proxy-agent/proxy-agent.strict-ssl.spec.ts @@ -31,6 +31,6 @@ describe('untrusted certificate', () => { httpsProxy: `http://127.0.0.1:${proxyPort}`, strictSsl: true, }) - await expect(fetch(url, { agent })).rejects.toThrow(/self signed certificate/) + await expect(fetch(url, { agent })).rejects.toThrow(/self[- ]signed certificate/); }) }) From 3254db99d173b74356dbd0d47ee2531ac81b3df8 Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Wed, 17 Jan 2024 13:03:49 +0100 Subject: [PATCH 05/33] style: remove unused `@ts-expect-error` --- network/proxy-agent/proxy-agent.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/network/proxy-agent/proxy-agent.ts b/network/proxy-agent/proxy-agent.ts index a966e0f..f2cd7c0 100644 --- a/network/proxy-agent/proxy-agent.ts +++ b/network/proxy-agent/proxy-agent.ts @@ -155,12 +155,10 @@ class PatchedHttpsProxyAgent extends HttpsProxyAgent { constructor (opts: any) { super(opts) - // @ts-expect-error this[extraOpts] = opts } callback (req: any, opts: any) { - // @ts-expect-error return super.callback(req, { ...this[extraOpts], ...opts }) } } From 739962b60f96ff1581ee23534bff438ec091d244 Mon Sep 17 00:00:00 2001 From: Nacho Aldama Date: Thu, 18 Jan 2024 01:51:30 +0100 Subject: [PATCH 06/33] fix: use Powershell in windows CI (#16) * fix: use Powershell in windows CI * revert: change step names * fix: set OS PATH in different steps * fix: set Windows PATH * fix: use Powershell v5.1 * fix: specify bvm path manually * fix: dont modify windows PATH * fix: set BVM path in PATH * fix: set user profile automatically --- .github/workflows/ci.yml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1fc15a0..7e72d95 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,19 @@ jobs: cache: 'pnpm' - name: Install Bit run: pnpm dlx @teambit/bvm install - - name: bit install - run: export PATH=$HOME/bin:$PATH && bit install - - name: Test - run: export PATH=$HOME/bin:$PATH && bit test + + - name: Set PATH for Unix + if: runner.os != 'Windows' + run: echo "PATH=$HOME/bin:$PATH" >> $GITHUB_ENV + + - name: Set PATH for Windows + if: runner.os == 'Windows' + shell: pwsh + run: | + echo "$env:USERPROFILE\AppData\Local\.bvm" | Out-File -FilePath $env:GITHUB_PATH -Append + + - name: Bit Install + run: bit install + + - name: Bit Test + run: bit test From b8b6bdbfd3370633bc41b93c68c5cd0abafbef03 Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Thu, 18 Jan 2024 11:46:08 +0100 Subject: [PATCH 07/33] refactor: replace `uriToHost ` with `nerf-dart` --- network/agent/agent.ts | 189 +++++++++++++++++++++-------------------- pnpm-lock.yaml | 7 ++ workspace.jsonc | 3 +- 3 files changed, 107 insertions(+), 92 deletions(-) diff --git a/network/agent/agent.ts b/network/agent/agent.ts index 32d431b..c4abea4 100644 --- a/network/agent/agent.ts +++ b/network/agent/agent.ts @@ -1,107 +1,114 @@ -import { URL } from 'url' -import HttpAgent from 'agentkeepalive' -import LRU from 'lru-cache' -import { getProxyAgent, ProxyAgentOptions } from '@pnpm/network.proxy-agent' +import { URL } from "url"; +import HttpAgent from "agentkeepalive"; +import LRU from "lru-cache"; +import nerfDart from "nerf-dart"; +import { getProxyAgent, ProxyAgentOptions } from "@pnpm/network.proxy-agent"; -const HttpsAgent = HttpAgent.HttpsAgent +const HttpsAgent = HttpAgent.HttpsAgent; -const DEFAULT_MAX_SOCKETS = 50 +const DEFAULT_MAX_SOCKETS = 50; -const AGENT_CACHE = new LRU({ max: 50 }) +const AGENT_CACHE = new LRU({ max: 50 }); export type AgentOptions = ProxyAgentOptions & { - noProxy?: boolean | string -} - -function uriToHost (uri: string) { - const parsedUri = new URL(uri) - const port = parsedUri.port - const hostname = parsedUri.hostname - let url = `//${hostname}` - if (port) { - url += `:${port}` - } - return `${url}/` -} + noProxy?: boolean | string; +}; -export function getAgent (uri: string, opts: AgentOptions) { - if ((opts.httpProxy || opts.httpsProxy) && !checkNoProxy(uri, opts)) { - const proxyAgent = getProxyAgent(uri, opts) - if (proxyAgent) return proxyAgent - } - return getNonProxyAgent(uri, opts) +export function getAgent(uri: string, opts: AgentOptions) { + if ((opts.httpProxy || opts.httpsProxy) && !checkNoProxy(uri, opts)) { + const proxyAgent = getProxyAgent(uri, opts); + if (proxyAgent) return proxyAgent; + } + return getNonProxyAgent(uri, opts); } -function getNonProxyAgent (uri: string, opts: AgentOptions) { - const parsedUri = new URL(uri) - const host = uriToHost(uri) - const isHttps = parsedUri.protocol === 'https:' +function getNonProxyAgent(uri: string, opts: AgentOptions) { + const parsedUri = new URL(uri); + const host = nerfDart(uri); + const isHttps = parsedUri.protocol === "https:"; - const clientCertificates = opts.clientCertificates?.[host] ?? null + const clientCertificates = opts.clientCertificates?.[host] ?? null; - /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ - const key = [ - `https:${isHttps.toString()}`, - `local-address:${opts.localAddress ?? '>no-local-address<'}`, - `strict-ssl:${isHttps ? Boolean(opts.strictSsl).toString() : '>no-strict-ssl<'}`, - `ca:${(isHttps && clientCertificates?.ca || opts.ca?.toString()) || '>no-ca<'}`, - `cert:${(isHttps && clientCertificates?.cert || opts.cert?.toString()) || '>no-cert<'}`, - `key:${(isHttps && clientCertificates?.key || opts.key) || '>no-key<'}`, - ].join(':') - /* eslint-enable @typescript-eslint/prefer-nullish-coalescing */ + /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ + const key = [ + `https:${isHttps.toString()}`, + `local-address:${opts.localAddress ?? ">no-local-address<"}`, + `strict-ssl:${ + isHttps ? Boolean(opts.strictSsl).toString() : ">no-strict-ssl<" + }`, + `ca:${ + (isHttps && clientCertificates?.ca) || opts.ca?.toString() || ">no-ca<" + }`, + `cert:${ + (isHttps && clientCertificates?.cert) || + opts.cert?.toString() || + ">no-cert<" + }`, + `key:${(isHttps && clientCertificates?.key) || opts.key || ">no-key<"}`, + ].join(":"); + /* eslint-enable @typescript-eslint/prefer-nullish-coalescing */ - if (AGENT_CACHE.peek(key)) { - return AGENT_CACHE.get(key) - } + if (AGENT_CACHE.peek(key)) { + return AGENT_CACHE.get(key); + } - // If opts.timeout is zero, set the agentTimeout to zero as well. A timeout - // of zero disables the timeout behavior (OS limits still apply). Else, if - // opts.timeout is a non-zero value, set it to timeout + 1, to ensure that - // the node-fetch-npm timeout will always fire first, giving us more - // consistent errors. - const agentTimeout = typeof opts.timeout !== 'number' || opts.timeout === 0 ? 0 : opts.timeout + 1 + // If opts.timeout is zero, set the agentTimeout to zero as well. A timeout + // of zero disables the timeout behavior (OS limits still apply). Else, if + // opts.timeout is a non-zero value, set it to timeout + 1, to ensure that + // the node-fetch-npm timeout will always fire first, giving us more + // consistent errors. + const agentTimeout = + typeof opts.timeout !== "number" || opts.timeout === 0 + ? 0 + : opts.timeout + 1; - // NOTE: localAddress is passed to the agent here even though it is an - // undocumented option of the agent's constructor. - // - // This works because all options of the agent are merged with - // all options of the request: - // https://github.com/nodejs/node/blob/350a95b89faab526de852d417bbb8a3ac823c325/lib/_http_agent.js#L254 - const agent = isHttps - ? new HttpsAgent({ - ca: clientCertificates?.ca || opts.ca, - cert: clientCertificates?.cert || opts.cert, - key: clientCertificates?.key || opts.key, - localAddress: opts.localAddress, - maxSockets: opts.maxSockets ?? DEFAULT_MAX_SOCKETS, - rejectUnauthorized: opts.strictSsl, - timeout: agentTimeout, - } as any) // eslint-disable-line @typescript-eslint/no-explicit-any - : new HttpAgent({ - localAddress: opts.localAddress, - maxSockets: opts.maxSockets ?? DEFAULT_MAX_SOCKETS, - timeout: agentTimeout, - } as any) // eslint-disable-line @typescript-eslint/no-explicit-any - AGENT_CACHE.set(key, agent) - return agent + // NOTE: localAddress is passed to the agent here even though it is an + // undocumented option of the agent's constructor. + // + // This works because all options of the agent are merged with + // all options of the request: + // https://github.com/nodejs/node/blob/350a95b89faab526de852d417bbb8a3ac823c325/lib/_http_agent.js#L254 + const agent = isHttps + ? new HttpsAgent({ + ca: clientCertificates?.ca || opts.ca, + cert: clientCertificates?.cert || opts.cert, + key: clientCertificates?.key || opts.key, + localAddress: opts.localAddress, + maxSockets: opts.maxSockets ?? DEFAULT_MAX_SOCKETS, + rejectUnauthorized: opts.strictSsl, + timeout: agentTimeout, + } as any) // eslint-disable-line @typescript-eslint/no-explicit-any + : new HttpAgent({ + localAddress: opts.localAddress, + maxSockets: opts.maxSockets ?? DEFAULT_MAX_SOCKETS, + timeout: agentTimeout, + } as any); // eslint-disable-line @typescript-eslint/no-explicit-any + AGENT_CACHE.set(key, agent); + return agent; } -function checkNoProxy (uri: string, opts: { noProxy?: boolean | string }) { - const host = new URL(uri).hostname.split('.').filter(x => x).reverse() - if (typeof opts.noProxy === 'string') { - const noproxyArr = opts.noProxy.split(/\s*,\s*/g) - return noproxyArr.some(no => { - const noParts = no.split('.').filter(x => x).reverse() - if (noParts.length === 0) { - return false - } - for (let i = 0; i < noParts.length; i++) { - if (host[i] !== noParts[i]) { - return false - } - } - return true - }) - } - return opts.noProxy +function checkNoProxy(uri: string, opts: { noProxy?: boolean | string }) { + const host = new URL(uri).hostname + .split(".") + .filter((x) => x) + .reverse(); + if (typeof opts.noProxy === "string") { + const noproxyArr = opts.noProxy.split(/\s*,\s*/g); + return noproxyArr.some((no) => { + const noParts = no + .split(".") + .filter((x) => x) + .reverse(); + if (noParts.length === 0) { + return false; + } + for (let i = 0; i < noParts.length; i++) { + if (host[i] !== noParts[i]) { + return false; + } + } + return true; + }); + } + return opts.noProxy; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 08db0a2..61a5193 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: lru-cache: specifier: 7.10.1 version: registry.npmjs.org/lru-cache@7.10.1 + nerf-dart: + specifier: ^1.0.0 + version: 1.0.0 safe-execa: specifier: 0.1.1 version: registry.npmjs.org/safe-execa@0.1.1 @@ -2026,6 +2029,10 @@ packages: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} dev: true + /nerf-dart@1.0.0: + resolution: {integrity: sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==} + dev: false + /next-tick@1.1.0: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} dev: true diff --git a/workspace.jsonc b/workspace.jsonc index 11b875f..8ab577f 100644 --- a/workspace.jsonc +++ b/workspace.jsonc @@ -49,6 +49,7 @@ "http-proxy-agent": "5.0.0", "https-proxy-agent": "5.0.1", "lru-cache": "7.10.1", + "nerf-dart": "^1.0.0", "node-fetch": "2.6.7", "proxy": "1.0.2", "safe-execa": "0.1.1", @@ -92,4 +93,4 @@ } } } -} \ No newline at end of file +} From 2cfe141bdbfebd7fad8a3a9981004475e86394e2 Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Thu, 18 Jan 2024 12:37:33 +0100 Subject: [PATCH 08/33] test: add tests --- network/agent/agent.spec.ts | 203 ++++++++++++++++++++++-------------- 1 file changed, 123 insertions(+), 80 deletions(-) diff --git a/network/agent/agent.spec.ts b/network/agent/agent.spec.ts index 6d0321e..8aedd4c 100644 --- a/network/agent/agent.spec.ts +++ b/network/agent/agent.spec.ts @@ -1,91 +1,134 @@ -import { getAgent } from './agent' +import { getAgent } from "./agent"; -jest.mock('agentkeepalive', () => { - const MockHttp = mockHttpAgent('http') - MockHttp['HttpsAgent'] = mockHttpAgent('https') - return MockHttp -}) -jest.mock('https-proxy-agent', () => mockHttpAgent('https-proxy')) +jest.mock("agentkeepalive", () => { + const MockHttp = mockHttpAgent("http"); + MockHttp["HttpsAgent"] = mockHttpAgent("https"); + return MockHttp; +}); +jest.mock("https-proxy-agent", () => mockHttpAgent("https-proxy")); -function mockHttpAgent (type: string) { - return function Agent (opts: any) { // eslint-disable-line @typescript-eslint/no-explicit-any - return { - ...opts, - __type: type, - } - } +function mockHttpAgent(type: string) { + return function Agent(opts: any) { + // eslint-disable-line @typescript-eslint/no-explicit-any + return { + ...opts, + __type: type, + }; + }; } const OPTS = { - agent: null, - ca: 'ca', - cert: 'cert', - key: 'key', - localAddress: 'localAddress', - maxSockets: 5, - strictSsl: true, - timeout: 5, -} + agent: null, + ca: "ca", + cert: "cert", + key: "key", + localAddress: "localAddress", + maxSockets: 5, + strictSsl: true, + timeout: 5, +}; -test('all expected options passed down to HttpAgent', () => { - expect(getAgent('http://foo.com/bar', OPTS)).toEqual({ - __type: 'http', - localAddress: 'localAddress', - maxSockets: 5, - timeout: 6, - }) -}) +test("all expected options passed down to HttpAgent", () => { + expect(getAgent("http://foo.com/bar", OPTS)).toEqual({ + __type: "http", + localAddress: "localAddress", + maxSockets: 5, + timeout: 6, + }); +}); -test('all expected options passed down to HttpsAgent', () => { - expect(getAgent('https://foo.com/bar', OPTS)).toEqual({ - __type: 'https', - ca: 'ca', - cert: 'cert', - key: 'key', - localAddress: 'localAddress', - maxSockets: 5, - rejectUnauthorized: true, - timeout: 6, - }) -}) +test("all expected options passed down to HttpsAgent", () => { + expect(getAgent("https://foo.com/bar", OPTS)).toEqual({ + __type: "https", + ca: "ca", + cert: "cert", + key: "key", + localAddress: "localAddress", + maxSockets: 5, + rejectUnauthorized: true, + timeout: 6, + }); +}); -test('all expected options passed down to proxy agent', () => { - const opts = { - httpsProxy: 'https://user:pass@my.proxy:1234/foo', - noProxy: 'qar.com, bar.com', - ...OPTS, - } - expect((getAgent('https://foo.com/bar', opts) as any).proxy).toEqual({ - ALPNProtocols: ['http 1.1'], - auth: 'user:pass', - ca: 'ca', - cert: 'cert', - host: 'my.proxy', - key: 'key', - localAddress: 'localAddress', - maxSockets: 5, - port: 1234, - protocol: 'https:', - rejectUnauthorized: true, - timeout: 6, - }) -}) +test("all expected options passed down to proxy agent", () => { + const opts = { + httpsProxy: "https://user:pass@my.proxy:1234/foo", + noProxy: "qar.com, bar.com", + ...OPTS, + }; + expect((getAgent("https://foo.com/bar", opts) as any).proxy).toEqual({ + ALPNProtocols: ["http 1.1"], + auth: "user:pass", + ca: "ca", + cert: "cert", + host: "my.proxy", + key: "key", + localAddress: "localAddress", + maxSockets: 5, + port: 1234, + protocol: "https:", + rejectUnauthorized: true, + timeout: 6, + }); +}); test("don't use a proxy when the URL is in noProxy", () => { - const opts = { - httpsProxy: 'https://user:pass@my.proxy:1234/foo', - noProxy: 'foo.com, bar.com', - ...OPTS, - } - expect(getAgent('https://foo.com/bar', opts)).toEqual({ - __type: 'https', - ca: 'ca', - cert: 'cert', - key: 'key', - localAddress: 'localAddress', - maxSockets: 5, - rejectUnauthorized: true, - timeout: 6, - }) -}) + const opts = { + httpsProxy: "https://user:pass@my.proxy:1234/foo", + noProxy: "foo.com, bar.com", + ...OPTS, + }; + expect(getAgent("https://foo.com/bar", opts)).toEqual({ + __type: "https", + ca: "ca", + cert: "cert", + key: "key", + localAddress: "localAddress", + maxSockets: 5, + rejectUnauthorized: true, + timeout: 6, + }); +}); + +test("should return the correct client certificates", () => { + const agent = getAgent("https://foo.com/bar", { + clientCertificates: { + "//foo.com/": { + ca: "ca", + cert: "cert", + key: "key", + }, + }, + }); + + expect(agent).toEqual({ + ca: "ca", + cert: "cert", + key: "key", + localAddress: undefined, + maxSockets: 50, + rejectUnauthorized: undefined, + timeout: 0, + __type: "https", + }); +}); + +test("should not return client certificates for a different host", () => { + const agent = getAgent("https://foo.com/bar", { + clientCertificates: { + "//bar.com/": { + ca: "ca", + cert: "cert", + key: "key", + }, + }, + }); + expect(agent).toEqual({ + localAddress: undefined, + maxSockets: 50, + rejectUnauthorized: undefined, + timeout: 0, + __type: "https", + }); +}); From d57f7914bff8fd04419a038a3f794013e56d8f6d Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Thu, 18 Jan 2024 21:17:55 +0100 Subject: [PATCH 09/33] style: update --- network/agent/agent.spec.ts | 238 ++++++++++++++--------------- network/agent/agent.ts | 190 +++++++++++------------ network/proxy-agent/proxy-agent.ts | 45 +++--- 3 files changed, 239 insertions(+), 234 deletions(-) diff --git a/network/agent/agent.spec.ts b/network/agent/agent.spec.ts index 8aedd4c..8010a3f 100644 --- a/network/agent/agent.spec.ts +++ b/network/agent/agent.spec.ts @@ -1,134 +1,134 @@ -import { getAgent } from "./agent"; +import { getAgent } from './agent' -jest.mock("agentkeepalive", () => { - const MockHttp = mockHttpAgent("http"); - MockHttp["HttpsAgent"] = mockHttpAgent("https"); - return MockHttp; -}); -jest.mock("https-proxy-agent", () => mockHttpAgent("https-proxy")); +jest.mock('agentkeepalive', () => { + const MockHttp = mockHttpAgent('http') + MockHttp['HttpsAgent'] = mockHttpAgent('https') + return MockHttp +}) +jest.mock('https-proxy-agent', () => mockHttpAgent('https-proxy')) function mockHttpAgent(type: string) { - return function Agent(opts: any) { - // eslint-disable-line @typescript-eslint/no-explicit-any - return { - ...opts, - __type: type, - }; - }; + return function Agent(opts: any) { + // eslint-disable-line @typescript-eslint/no-explicit-any + return { + ...opts, + __type: type, + } + } } const OPTS = { - agent: null, - ca: "ca", - cert: "cert", - key: "key", - localAddress: "localAddress", - maxSockets: 5, - strictSsl: true, - timeout: 5, -}; + agent: null, + ca: 'ca', + cert: 'cert', + key: 'key', + localAddress: 'localAddress', + maxSockets: 5, + strictSsl: true, + timeout: 5, +} -test("all expected options passed down to HttpAgent", () => { - expect(getAgent("http://foo.com/bar", OPTS)).toEqual({ - __type: "http", - localAddress: "localAddress", - maxSockets: 5, - timeout: 6, - }); -}); +test('all expected options passed down to HttpAgent', () => { + expect(getAgent('http://foo.com/bar', OPTS)).toEqual({ + __type: 'http', + localAddress: 'localAddress', + maxSockets: 5, + timeout: 6, + }) +}) -test("all expected options passed down to HttpsAgent", () => { - expect(getAgent("https://foo.com/bar", OPTS)).toEqual({ - __type: "https", - ca: "ca", - cert: "cert", - key: "key", - localAddress: "localAddress", - maxSockets: 5, - rejectUnauthorized: true, - timeout: 6, - }); -}); +test('all expected options passed down to HttpsAgent', () => { + expect(getAgent('https://foo.com/bar', OPTS)).toEqual({ + __type: 'https', + ca: 'ca', + cert: 'cert', + key: 'key', + localAddress: 'localAddress', + maxSockets: 5, + rejectUnauthorized: true, + timeout: 6, + }) +}) -test("all expected options passed down to proxy agent", () => { - const opts = { - httpsProxy: "https://user:pass@my.proxy:1234/foo", - noProxy: "qar.com, bar.com", - ...OPTS, - }; - expect((getAgent("https://foo.com/bar", opts) as any).proxy).toEqual({ - ALPNProtocols: ["http 1.1"], - auth: "user:pass", - ca: "ca", - cert: "cert", - host: "my.proxy", - key: "key", - localAddress: "localAddress", - maxSockets: 5, - port: 1234, - protocol: "https:", - rejectUnauthorized: true, - timeout: 6, - }); -}); +test('all expected options passed down to proxy agent', () => { + const opts = { + httpsProxy: 'https://user:pass@my.proxy:1234/foo', + noProxy: 'qar.com, bar.com', + ...OPTS, + } + expect((getAgent('https://foo.com/bar', opts) as any).proxy).toEqual({ + ALPNProtocols: ['http 1.1'], + auth: 'user:pass', + ca: 'ca', + cert: 'cert', + host: 'my.proxy', + key: 'key', + localAddress: 'localAddress', + maxSockets: 5, + port: 1234, + protocol: 'https:', + rejectUnauthorized: true, + timeout: 6, + }) +}) test("don't use a proxy when the URL is in noProxy", () => { - const opts = { - httpsProxy: "https://user:pass@my.proxy:1234/foo", - noProxy: "foo.com, bar.com", - ...OPTS, - }; - expect(getAgent("https://foo.com/bar", opts)).toEqual({ - __type: "https", - ca: "ca", - cert: "cert", - key: "key", - localAddress: "localAddress", - maxSockets: 5, - rejectUnauthorized: true, - timeout: 6, - }); -}); + const opts = { + httpsProxy: 'https://user:pass@my.proxy:1234/foo', + noProxy: 'foo.com, bar.com', + ...OPTS, + } + expect(getAgent('https://foo.com/bar', opts)).toEqual({ + __type: 'https', + ca: 'ca', + cert: 'cert', + key: 'key', + localAddress: 'localAddress', + maxSockets: 5, + rejectUnauthorized: true, + timeout: 6, + }) +}) -test("should return the correct client certificates", () => { - const agent = getAgent("https://foo.com/bar", { - clientCertificates: { - "//foo.com/": { - ca: "ca", - cert: "cert", - key: "key", - }, - }, - }); +test('should return the correct client certificates', () => { + const agent = getAgent('https://foo.com/bar', { + clientCertificates: { + '//foo.com/': { + ca: 'ca', + cert: 'cert', + key: 'key', + }, + }, + }) - expect(agent).toEqual({ - ca: "ca", - cert: "cert", - key: "key", - localAddress: undefined, - maxSockets: 50, - rejectUnauthorized: undefined, - timeout: 0, - __type: "https", - }); -}); + expect(agent).toEqual({ + ca: 'ca', + cert: 'cert', + key: 'key', + localAddress: undefined, + maxSockets: 50, + rejectUnauthorized: undefined, + timeout: 0, + __type: 'https', + }) +}) -test("should not return client certificates for a different host", () => { - const agent = getAgent("https://foo.com/bar", { - clientCertificates: { - "//bar.com/": { - ca: "ca", - cert: "cert", - key: "key", - }, - }, - }); +test('should not return client certificates for a different host', () => { + const agent = getAgent('https://foo.com/bar', { + clientCertificates: { + '//bar.com/': { + ca: 'ca', + cert: 'cert', + key: 'key', + }, + }, + }) - expect(agent).toEqual({ - localAddress: undefined, - maxSockets: 50, - rejectUnauthorized: undefined, - timeout: 0, - __type: "https", - }); -}); + expect(agent).toEqual({ + localAddress: undefined, + maxSockets: 50, + rejectUnauthorized: undefined, + timeout: 0, + __type: 'https', + }) +}) diff --git a/network/agent/agent.ts b/network/agent/agent.ts index c4abea4..4a37f98 100644 --- a/network/agent/agent.ts +++ b/network/agent/agent.ts @@ -1,114 +1,114 @@ -import { URL } from "url"; -import HttpAgent from "agentkeepalive"; -import LRU from "lru-cache"; -import nerfDart from "nerf-dart"; -import { getProxyAgent, ProxyAgentOptions } from "@pnpm/network.proxy-agent"; +import { URL } from 'url' +import HttpAgent from 'agentkeepalive' +import LRU from 'lru-cache' +import nerfDart from 'nerf-dart' +import { getProxyAgent, ProxyAgentOptions } from '@pnpm/network.proxy-agent' -const HttpsAgent = HttpAgent.HttpsAgent; +const HttpsAgent = HttpAgent.HttpsAgent -const DEFAULT_MAX_SOCKETS = 50; +const DEFAULT_MAX_SOCKETS = 50 -const AGENT_CACHE = new LRU({ max: 50 }); +const AGENT_CACHE = new LRU({ max: 50 }) export type AgentOptions = ProxyAgentOptions & { - noProxy?: boolean | string; -}; + noProxy?: boolean | string +} export function getAgent(uri: string, opts: AgentOptions) { - if ((opts.httpProxy || opts.httpsProxy) && !checkNoProxy(uri, opts)) { - const proxyAgent = getProxyAgent(uri, opts); - if (proxyAgent) return proxyAgent; - } - return getNonProxyAgent(uri, opts); + if ((opts.httpProxy || opts.httpsProxy) && !checkNoProxy(uri, opts)) { + const proxyAgent = getProxyAgent(uri, opts) + if (proxyAgent) return proxyAgent + } + return getNonProxyAgent(uri, opts) } function getNonProxyAgent(uri: string, opts: AgentOptions) { - const parsedUri = new URL(uri); - const host = nerfDart(uri); - const isHttps = parsedUri.protocol === "https:"; + const parsedUri = new URL(uri) + const host = nerfDart(uri) + const isHttps = parsedUri.protocol === 'https:' - const clientCertificates = opts.clientCertificates?.[host] ?? null; + const clientCertificates = opts.clientCertificates?.[host] ?? null - /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ - const key = [ - `https:${isHttps.toString()}`, - `local-address:${opts.localAddress ?? ">no-local-address<"}`, - `strict-ssl:${ - isHttps ? Boolean(opts.strictSsl).toString() : ">no-strict-ssl<" - }`, - `ca:${ - (isHttps && clientCertificates?.ca) || opts.ca?.toString() || ">no-ca<" - }`, - `cert:${ - (isHttps && clientCertificates?.cert) || - opts.cert?.toString() || - ">no-cert<" - }`, - `key:${(isHttps && clientCertificates?.key) || opts.key || ">no-key<"}`, - ].join(":"); - /* eslint-enable @typescript-eslint/prefer-nullish-coalescing */ + /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ + const key = [ + `https:${isHttps.toString()}`, + `local-address:${opts.localAddress ?? '>no-local-address<'}`, + `strict-ssl:${ + isHttps ? Boolean(opts.strictSsl).toString() : '>no-strict-ssl<' + }`, + `ca:${ + (isHttps && clientCertificates?.ca) || opts.ca?.toString() || '>no-ca<' + }`, + `cert:${ + (isHttps && clientCertificates?.cert) || + opts.cert?.toString() || + '>no-cert<' + }`, + `key:${(isHttps && clientCertificates?.key) || opts.key || '>no-key<'}`, + ].join(':') + /* eslint-enable @typescript-eslint/prefer-nullish-coalescing */ - if (AGENT_CACHE.peek(key)) { - return AGENT_CACHE.get(key); - } + if (AGENT_CACHE.peek(key)) { + return AGENT_CACHE.get(key) + } - // If opts.timeout is zero, set the agentTimeout to zero as well. A timeout - // of zero disables the timeout behavior (OS limits still apply). Else, if - // opts.timeout is a non-zero value, set it to timeout + 1, to ensure that - // the node-fetch-npm timeout will always fire first, giving us more - // consistent errors. - const agentTimeout = - typeof opts.timeout !== "number" || opts.timeout === 0 - ? 0 - : opts.timeout + 1; + // If opts.timeout is zero, set the agentTimeout to zero as well. A timeout + // of zero disables the timeout behavior (OS limits still apply). Else, if + // opts.timeout is a non-zero value, set it to timeout + 1, to ensure that + // the node-fetch-npm timeout will always fire first, giving us more + // consistent errors. + const agentTimeout = + typeof opts.timeout !== 'number' || opts.timeout === 0 + ? 0 + : opts.timeout + 1 - // NOTE: localAddress is passed to the agent here even though it is an - // undocumented option of the agent's constructor. - // - // This works because all options of the agent are merged with - // all options of the request: - // https://github.com/nodejs/node/blob/350a95b89faab526de852d417bbb8a3ac823c325/lib/_http_agent.js#L254 - const agent = isHttps - ? new HttpsAgent({ - ca: clientCertificates?.ca || opts.ca, - cert: clientCertificates?.cert || opts.cert, - key: clientCertificates?.key || opts.key, - localAddress: opts.localAddress, - maxSockets: opts.maxSockets ?? DEFAULT_MAX_SOCKETS, - rejectUnauthorized: opts.strictSsl, - timeout: agentTimeout, - } as any) // eslint-disable-line @typescript-eslint/no-explicit-any - : new HttpAgent({ - localAddress: opts.localAddress, - maxSockets: opts.maxSockets ?? DEFAULT_MAX_SOCKETS, - timeout: agentTimeout, - } as any); // eslint-disable-line @typescript-eslint/no-explicit-any - AGENT_CACHE.set(key, agent); - return agent; + // NOTE: localAddress is passed to the agent here even though it is an + // undocumented option of the agent's constructor. + // + // This works because all options of the agent are merged with + // all options of the request: + // https://github.com/nodejs/node/blob/350a95b89faab526de852d417bbb8a3ac823c325/lib/_http_agent.js#L254 + const agent = isHttps + ? new HttpsAgent({ + ca: clientCertificates?.ca || opts.ca, + cert: clientCertificates?.cert || opts.cert, + key: clientCertificates?.key || opts.key, + localAddress: opts.localAddress, + maxSockets: opts.maxSockets ?? DEFAULT_MAX_SOCKETS, + rejectUnauthorized: opts.strictSsl, + timeout: agentTimeout, + } as any) // eslint-disable-line @typescript-eslint/no-explicit-any + : new HttpAgent({ + localAddress: opts.localAddress, + maxSockets: opts.maxSockets ?? DEFAULT_MAX_SOCKETS, + timeout: agentTimeout, + } as any) // eslint-disable-line @typescript-eslint/no-explicit-any + AGENT_CACHE.set(key, agent) + return agent } function checkNoProxy(uri: string, opts: { noProxy?: boolean | string }) { - const host = new URL(uri).hostname - .split(".") - .filter((x) => x) - .reverse(); - if (typeof opts.noProxy === "string") { - const noproxyArr = opts.noProxy.split(/\s*,\s*/g); - return noproxyArr.some((no) => { - const noParts = no - .split(".") - .filter((x) => x) - .reverse(); - if (noParts.length === 0) { - return false; - } - for (let i = 0; i < noParts.length; i++) { - if (host[i] !== noParts[i]) { - return false; - } - } - return true; - }); - } - return opts.noProxy; + const host = new URL(uri).hostname + .split('.') + .filter(x => x) + .reverse() + if (typeof opts.noProxy === 'string') { + const noproxyArr = opts.noProxy.split(/\s*,\s*/g) + return noproxyArr.some(no => { + const noParts = no + .split('.') + .filter(x => x) + .reverse() + if (noParts.length === 0) { + return false + } + for (let i = 0; i < noParts.length; i++) { + if (host[i] !== noParts[i]) { + return false + } + } + return true + }) + } + return opts.noProxy } diff --git a/network/proxy-agent/proxy-agent.ts b/network/proxy-agent/proxy-agent.ts index f2cd7c0..d716a4c 100644 --- a/network/proxy-agent/proxy-agent.ts +++ b/network/proxy-agent/proxy-agent.ts @@ -21,14 +21,14 @@ export interface ProxyAgentOptions { timeout?: number clientCertificates?: { [registryUrl: string]: { - cert: string, - key: string, - ca?: string, - }, + cert: string + key: string + ca?: string + } } } -export function getProxyAgent (uri: string, opts: ProxyAgentOptions) { +export function getProxyAgent(uri: string, opts: ProxyAgentOptions) { const parsedUri = new URL(uri) const pxuri = getProxyUri(parsedUri, opts) if (!pxuri) return @@ -39,7 +39,9 @@ export function getProxyAgent (uri: string, opts: ProxyAgentOptions) { `https:${isHttps.toString()}`, `proxy:${pxuri.protocol}//${pxuri.username}:${pxuri.password}@${pxuri.host}:${pxuri.port}`, `local-address:${opts.localAddress ?? '>no-local-address<'}`, - `strict-ssl:${isHttps ? Boolean(opts.strictSsl).toString() : '>no-strict-ssl<'}`, + `strict-ssl:${ + isHttps ? Boolean(opts.strictSsl).toString() : '>no-strict-ssl<' + }`, `ca:${(isHttps && opts.ca?.toString()) || '>no-ca<'}`, `cert:${(isHttps && opts.cert?.toString()) || '>no-cert<'}`, `key:${(isHttps && opts.key) || '>no-key<'}`, @@ -54,7 +56,7 @@ export function getProxyAgent (uri: string, opts: ProxyAgentOptions) { return proxy } -function getProxyUri ( +function getProxyUri( uri: URL, opts: { httpProxy?: string @@ -65,14 +67,14 @@ function getProxyUri ( let proxy: string | undefined switch (protocol) { - case 'http:': { - proxy = opts.httpProxy - break - } - case 'https:': { - proxy = opts.httpsProxy - break - } + case 'http:': { + proxy = opts.httpProxy + break + } + case 'https:': { + proxy = opts.httpsProxy + break + } } if (!proxy) { @@ -96,7 +98,7 @@ function getProxyUri ( } } -function getProxy ( +function getProxy( proxyUrl: URL, opts: { ca?: string | string[] @@ -121,7 +123,10 @@ function getProxy ( port: proxyUrl.port, protocol: proxyUrl.protocol, rejectUnauthorized: opts.strictSsl, - timeout: typeof opts.timeout !== 'number' || opts.timeout === 0 ? 0 : opts.timeout + 1, + timeout: + typeof opts.timeout !== 'number' || opts.timeout === 0 + ? 0 + : opts.timeout + 1, } if (proxyUrl.protocol === 'http:' || proxyUrl.protocol === 'https:') { @@ -137,7 +142,7 @@ function getProxy ( return undefined } -function getAuth (user: { username?: string, password?: string }) { +function getAuth(user: { username?: string; password?: string }) { if (!user.username) { return undefined } @@ -152,13 +157,13 @@ const extraOpts = Symbol('extra agent opts') // This is a workaround for this issue: https://github.com/TooTallNate/node-https-proxy-agent/issues/89 class PatchedHttpsProxyAgent extends HttpsProxyAgent { - constructor (opts: any) { + constructor(opts: any) { super(opts) this[extraOpts] = opts } - callback (req: any, opts: any) { + callback(req: any, opts: any) { return super.callback(req, { ...this[extraOpts], ...opts }) } } From 48185aab773cff8c09bfa7003f89cdec1ad8aa76 Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Thu, 18 Jan 2024 21:20:26 +0100 Subject: [PATCH 10/33] style: update --- network/agent/agent.ts | 6 +++--- network/proxy-agent/proxy-agent.ts | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/network/agent/agent.ts b/network/agent/agent.ts index 4a37f98..512b9ae 100644 --- a/network/agent/agent.ts +++ b/network/agent/agent.ts @@ -14,7 +14,7 @@ export type AgentOptions = ProxyAgentOptions & { noProxy?: boolean | string } -export function getAgent(uri: string, opts: AgentOptions) { +export function getAgent (uri: string, opts: AgentOptions) { if ((opts.httpProxy || opts.httpsProxy) && !checkNoProxy(uri, opts)) { const proxyAgent = getProxyAgent(uri, opts) if (proxyAgent) return proxyAgent @@ -22,7 +22,7 @@ export function getAgent(uri: string, opts: AgentOptions) { return getNonProxyAgent(uri, opts) } -function getNonProxyAgent(uri: string, opts: AgentOptions) { +function getNonProxyAgent (uri: string, opts: AgentOptions) { const parsedUri = new URL(uri) const host = nerfDart(uri) const isHttps = parsedUri.protocol === 'https:' @@ -87,7 +87,7 @@ function getNonProxyAgent(uri: string, opts: AgentOptions) { return agent } -function checkNoProxy(uri: string, opts: { noProxy?: boolean | string }) { +function checkNoProxy (uri: string, opts: { noProxy?: boolean | string }) { const host = new URL(uri).hostname .split('.') .filter(x => x) diff --git a/network/proxy-agent/proxy-agent.ts b/network/proxy-agent/proxy-agent.ts index d716a4c..7cbca56 100644 --- a/network/proxy-agent/proxy-agent.ts +++ b/network/proxy-agent/proxy-agent.ts @@ -28,7 +28,7 @@ export interface ProxyAgentOptions { } } -export function getProxyAgent(uri: string, opts: ProxyAgentOptions) { +export function getProxyAgent (uri: string, opts: ProxyAgentOptions) { const parsedUri = new URL(uri) const pxuri = getProxyUri(parsedUri, opts) if (!pxuri) return @@ -56,7 +56,7 @@ export function getProxyAgent(uri: string, opts: ProxyAgentOptions) { return proxy } -function getProxyUri( +function getProxyUri ( uri: URL, opts: { httpProxy?: string @@ -98,7 +98,7 @@ function getProxyUri( } } -function getProxy( +function getProxy ( proxyUrl: URL, opts: { ca?: string | string[] @@ -142,7 +142,7 @@ function getProxy( return undefined } -function getAuth(user: { username?: string; password?: string }) { +function getAuth (user: { username?: string; password?: string }) { if (!user.username) { return undefined } @@ -157,13 +157,13 @@ const extraOpts = Symbol('extra agent opts') // This is a workaround for this issue: https://github.com/TooTallNate/node-https-proxy-agent/issues/89 class PatchedHttpsProxyAgent extends HttpsProxyAgent { - constructor(opts: any) { + constructor (opts: any) { super(opts) this[extraOpts] = opts } - callback(req: any, opts: any) { + callback (req: any, opts: any) { return super.callback(req, { ...this[extraOpts], ...opts }) } } From 06bf8598c77b4357718318e5acfa639ffe72c4ad Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Thu, 18 Jan 2024 22:32:56 +0100 Subject: [PATCH 11/33] style: update --- network/agent/agent.spec.ts | 2 +- network/proxy-agent/proxy-agent.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/network/agent/agent.spec.ts b/network/agent/agent.spec.ts index 8010a3f..4bcf1a9 100644 --- a/network/agent/agent.spec.ts +++ b/network/agent/agent.spec.ts @@ -7,7 +7,7 @@ jest.mock('agentkeepalive', () => { }) jest.mock('https-proxy-agent', () => mockHttpAgent('https-proxy')) -function mockHttpAgent(type: string) { +function mockHttpAgent (type: string) { return function Agent(opts: any) { // eslint-disable-line @typescript-eslint/no-explicit-any return { diff --git a/network/proxy-agent/proxy-agent.ts b/network/proxy-agent/proxy-agent.ts index 7cbca56..40a044f 100644 --- a/network/proxy-agent/proxy-agent.ts +++ b/network/proxy-agent/proxy-agent.ts @@ -142,7 +142,7 @@ function getProxy ( return undefined } -function getAuth (user: { username?: string; password?: string }) { +function getAuth (user: { username?: string, password?: string }) { if (!user.username) { return undefined } From af601206ac03d9ee433b7c12417a87f25b9ec8d3 Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Thu, 18 Jan 2024 22:33:35 +0100 Subject: [PATCH 12/33] style: update --- network/agent/agent.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/agent/agent.spec.ts b/network/agent/agent.spec.ts index 4bcf1a9..02925b1 100644 --- a/network/agent/agent.spec.ts +++ b/network/agent/agent.spec.ts @@ -8,7 +8,7 @@ jest.mock('agentkeepalive', () => { jest.mock('https-proxy-agent', () => mockHttpAgent('https-proxy')) function mockHttpAgent (type: string) { - return function Agent(opts: any) { + return function Agent (opts: any) { // eslint-disable-line @typescript-eslint/no-explicit-any return { ...opts, From 6f691da8717731686435537e1427670e48debd29 Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Thu, 18 Jan 2024 22:33:56 +0100 Subject: [PATCH 13/33] style: update --- network/agent/agent.spec.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/network/agent/agent.spec.ts b/network/agent/agent.spec.ts index 02925b1..eda7278 100644 --- a/network/agent/agent.spec.ts +++ b/network/agent/agent.spec.ts @@ -8,8 +8,7 @@ jest.mock('agentkeepalive', () => { jest.mock('https-proxy-agent', () => mockHttpAgent('https-proxy')) function mockHttpAgent (type: string) { - return function Agent (opts: any) { - // eslint-disable-line @typescript-eslint/no-explicit-any + return function Agent (opts: any) { // eslint-disable-line @typescript-eslint/no-explicit-any return { ...opts, __type: type, From ab232be7dd3f20feb8ff9e096185a50048c74d00 Mon Sep 17 00:00:00 2001 From: Nacho Aldama Date: Thu, 18 Jan 2024 23:36:03 +0100 Subject: [PATCH 14/33] fix: conditional if https Co-authored-by: Zoltan Kochan --- network/agent/agent.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/network/agent/agent.ts b/network/agent/agent.ts index 512b9ae..bba86be 100644 --- a/network/agent/agent.ts +++ b/network/agent/agent.ts @@ -37,14 +37,14 @@ function getNonProxyAgent (uri: string, opts: AgentOptions) { isHttps ? Boolean(opts.strictSsl).toString() : '>no-strict-ssl<' }`, `ca:${ - (isHttps && clientCertificates?.ca) || opts.ca?.toString() || '>no-ca<' + isHttps && (clientCertificates?.ca || opts.ca?.toString()) || '>no-ca<' }`, `cert:${ - (isHttps && clientCertificates?.cert) || - opts.cert?.toString() || + isHttps && (clientCertificates?.cert || + opts.cert?.toString()) || '>no-cert<' }`, - `key:${(isHttps && clientCertificates?.key) || opts.key || '>no-key<'}`, + `key:${isHttps && (clientCertificates?.key || opts.key) || '>no-key<'}`, ].join(':') /* eslint-enable @typescript-eslint/prefer-nullish-coalescing */ From d6d8b708e4e1071d88b66e93ee98f9a2e5854f61 Mon Sep 17 00:00:00 2001 From: Nacho Aldama Date: Thu, 18 Jan 2024 23:36:34 +0100 Subject: [PATCH 15/33] refactor: destructure client certificates Co-authored-by: Zoltan Kochan --- network/agent/agent.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/network/agent/agent.ts b/network/agent/agent.ts index bba86be..28f47d0 100644 --- a/network/agent/agent.ts +++ b/network/agent/agent.ts @@ -27,7 +27,10 @@ function getNonProxyAgent (uri: string, opts: AgentOptions) { const host = nerfDart(uri) const isHttps = parsedUri.protocol === 'https:' - const clientCertificates = opts.clientCertificates?.[host] ?? null + const { ca, cert, key } = { + ...opts, + ...opts.clientCertificates?.[host], + } /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ const key = [ From d7533644575e5f10b45b986fd2273c7bb8af1195 Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Fri, 19 Jan 2024 00:12:49 +0100 Subject: [PATCH 16/33] fix: type errors --- network/agent/agent.ts | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/network/agent/agent.ts b/network/agent/agent.ts index 28f47d0..f1d60a0 100644 --- a/network/agent/agent.ts +++ b/network/agent/agent.ts @@ -27,7 +27,7 @@ function getNonProxyAgent (uri: string, opts: AgentOptions) { const host = nerfDart(uri) const isHttps = parsedUri.protocol === 'https:' - const { ca, cert, key } = { + const { ca, cert, key: certKey } = { ...opts, ...opts.clientCertificates?.[host], } @@ -39,15 +39,9 @@ function getNonProxyAgent (uri: string, opts: AgentOptions) { `strict-ssl:${ isHttps ? Boolean(opts.strictSsl).toString() : '>no-strict-ssl<' }`, - `ca:${ - isHttps && (clientCertificates?.ca || opts.ca?.toString()) || '>no-ca<' - }`, - `cert:${ - isHttps && (clientCertificates?.cert || - opts.cert?.toString()) || - '>no-cert<' - }`, - `key:${isHttps && (clientCertificates?.key || opts.key) || '>no-key<'}`, + `ca:${isHttps && (ca?.toString()) || '>no-ca<'}`, + `cert:${isHttps && (cert?.toString()) || '>no-cert<'}`, + `key:${isHttps && (certKey?.toString()) || '>no-key<'}`, ].join(':') /* eslint-enable @typescript-eslint/prefer-nullish-coalescing */ @@ -73,9 +67,9 @@ function getNonProxyAgent (uri: string, opts: AgentOptions) { // https://github.com/nodejs/node/blob/350a95b89faab526de852d417bbb8a3ac823c325/lib/_http_agent.js#L254 const agent = isHttps ? new HttpsAgent({ - ca: clientCertificates?.ca || opts.ca, - cert: clientCertificates?.cert || opts.cert, - key: clientCertificates?.key || opts.key, + ca, + cert, + key: certKey, localAddress: opts.localAddress, maxSockets: opts.maxSockets ?? DEFAULT_MAX_SOCKETS, rejectUnauthorized: opts.strictSsl, From c08464bb5bf9ee464bee4da55445fcfabff2a711 Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Fri, 19 Jan 2024 00:23:13 +0100 Subject: [PATCH 17/33] test: add more tests --- network/agent/agent.spec.ts | 72 +++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/network/agent/agent.spec.ts b/network/agent/agent.spec.ts index eda7278..4ab2e8a 100644 --- a/network/agent/agent.spec.ts +++ b/network/agent/agent.spec.ts @@ -131,3 +131,75 @@ test('should not return client certificates for a different host', () => { __type: 'https', }) }) + +test('scoped certificates override global certificates', () => { + const agent = getAgent('https://foo.com/bar', { + ca: 'global-ca', + key: 'global-key', + cert: 'global-cert', + clientCertificates: { + '//foo.com/': { + ca: 'scoped-ca', + cert: 'scoped-cert', + key: 'scoped-key', + }, + }, + }) + + expect(agent).toEqual({ + ca: 'scoped-ca', + cert: 'scoped-cert', + key: 'scoped-key', + localAddress: undefined, + maxSockets: 50, + rejectUnauthorized: undefined, + timeout: 0, + __type: 'https', + }) +}) + +test('select correct client certificates when host has a port', () => { + const agent = getAgent('https://foo.com:1234/bar', { + clientCertificates: { + '//foo.com:1234/': { + ca: 'ca', + cert: 'cert', + key: 'key', + }, + }, + }) + + expect(agent).toEqual({ + ca: 'ca', + cert: 'cert', + key: 'key', + localAddress: undefined, + maxSockets: 50, + rejectUnauthorized: undefined, + timeout: 0, + __type: 'https', + }) +}) + +test('select correct client certificates when host has a path', () => { + const agent = getAgent('https://foo.com/bar/baz', { + clientCertificates: { + '//foo.com/bar/': { + ca: 'ca', + cert: 'cert', + key: 'key', + }, + }, + }) + + expect(agent).toEqual({ + ca: 'ca', + cert: 'cert', + key: 'key', + localAddress: undefined, + maxSockets: 50, + rejectUnauthorized: undefined, + timeout: 0, + __type: 'https', + }) +}) \ No newline at end of file From 14d9aa9b970b3cad7297f82e3a2816e932abc01e Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Thu, 25 Jan 2024 12:26:49 +0100 Subject: [PATCH 18/33] feat: add url component for URL specific functions --- .bitmap | 14 +++ network/agent/agent.spec.ts | 25 +++++- network/agent/agent.ts | 16 +++- network/url/index.ts | 2 + network/url/url.docs.mdx | 10 +++ network/url/url.spec.ts | 173 ++++++++++++++++++++++++++++++++++++ network/url/url.ts | 71 +++++++++++++++ 7 files changed, 307 insertions(+), 4 deletions(-) create mode 100644 network/url/index.ts create mode 100644 network/url/url.docs.mdx create mode 100644 network/url/url.spec.ts create mode 100644 network/url/url.ts diff --git a/.bitmap b/.bitmap index bc81831..5f71f05 100644 --- a/.bitmap +++ b/.bitmap @@ -161,5 +161,19 @@ } } }, + "url": { + "name": "url", + "scope": "", + "version": "", + "defaultScope": "pnpm.network", + "mainFile": "index.ts", + "rootDir": "network/url", + "config": { + "pnpm.env/envs/pnpm-env": {}, + "teambit.envs/envs": { + "env": "pnpm.env/envs/pnpm-env" + } + } + }, "$schema-version": "17.0.0" } \ No newline at end of file diff --git a/network/agent/agent.spec.ts b/network/agent/agent.spec.ts index 4ab2e8a..f26f6ee 100644 --- a/network/agent/agent.spec.ts +++ b/network/agent/agent.spec.ts @@ -184,7 +184,30 @@ test('select correct client certificates when host has a port', () => { test('select correct client certificates when host has a path', () => { const agent = getAgent('https://foo.com/bar/baz', { clientCertificates: { - '//foo.com/bar/': { + '//foo.com/': { + ca: 'ca', + cert: 'cert', + key: 'key', + }, + }, + }) + + expect(agent).toEqual({ + ca: 'ca', + cert: 'cert', + key: 'key', + localAddress: undefined, + maxSockets: 50, + rejectUnauthorized: undefined, + timeout: 0, + __type: 'https', + }) +}) + +test('select correct client certificates when host has a path and the cert contains a path', () => { + const agent = getAgent('https://foo.com/bar/baz', { + clientCertificates: { + '//foo.com/bar': { ca: 'ca', cert: 'cert', key: 'key', diff --git a/network/agent/agent.ts b/network/agent/agent.ts index f1d60a0..4d3996b 100644 --- a/network/agent/agent.ts +++ b/network/agent/agent.ts @@ -1,8 +1,8 @@ import { URL } from 'url' import HttpAgent from 'agentkeepalive' import LRU from 'lru-cache' -import nerfDart from 'nerf-dart' import { getProxyAgent, ProxyAgentOptions } from '@pnpm/network.proxy-agent' +import { parseUri } from '@pnpm/network.url'; const HttpsAgent = HttpAgent.HttpsAgent @@ -22,14 +22,24 @@ export function getAgent (uri: string, opts: AgentOptions) { return getNonProxyAgent(uri, opts) } +function getClientCertificates(uri: string, opts: AgentOptions) { + const { host, hostOnlyDomain, pathname } = parseUri(uri) + + if (host.endsWith(pathname)) { + return opts.clientCertificates?.[host]; + } + + const fullPath = `${host}${pathname !== '/' ? pathname : ''}`; + return opts.clientCertificates?.[fullPath] ?? opts.clientCertificates?.[host] ?? opts.clientCertificates?.[hostOnlyDomain]; +} + function getNonProxyAgent (uri: string, opts: AgentOptions) { const parsedUri = new URL(uri) - const host = nerfDart(uri) const isHttps = parsedUri.protocol === 'https:' const { ca, cert, key: certKey } = { ...opts, - ...opts.clientCertificates?.[host], + ...getClientCertificates(uri, opts), } /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ diff --git a/network/url/index.ts b/network/url/index.ts new file mode 100644 index 0000000..a8ea9e0 --- /dev/null +++ b/network/url/index.ts @@ -0,0 +1,2 @@ +export { parseUri } from './url'; +export type { ParsedUri } from './url'; diff --git a/network/url/url.docs.mdx b/network/url/url.docs.mdx new file mode 100644 index 0000000..cc25a35 --- /dev/null +++ b/network/url/url.docs.mdx @@ -0,0 +1,10 @@ +--- +labels: ['Url', 'module'] +description: 'A Url module.' +--- + +A url module. + +```ts +url(); +``` diff --git a/network/url/url.spec.ts b/network/url/url.spec.ts new file mode 100644 index 0000000..5123fcb --- /dev/null +++ b/network/url/url.spec.ts @@ -0,0 +1,173 @@ +import { parseUri } from './url'; + +describe('parseUri', () => { + it('should parse a simple URL', () => { + const uri = 'https://example.com'; + const expected = { + raw: uri, + protocol: 'https:', + nerf: '//example.com/', + host: 'example.com', + hostOnlyDomain: '//example.com/', + port: '', + pathname: '/', + search: '', + hash: '', + }; + const actual = parseUri(uri); + expect(actual).toEqual(expected); + }); + + it('should parse a URL with a port', () => { + const uri = 'https://example.com:8080'; + const expected = { + raw: uri, + protocol: 'https:', + nerf: '//example.com:8080/', + hostOnlyDomain: '//example.com:8080/', + host: 'example.com', + port: '8080', + pathname: '/', + search: '', + hash: '', + }; + const actual = parseUri(uri); + expect(actual).toEqual(expected); + }); + + it('should parse a URL with a search', () => { + const uri = 'https://example.com?foo=bar'; + const expected = { + raw: uri, + protocol: 'https:', + nerf: '//example.com/', + host: 'example.com', + hostOnlyDomain: '//example.com/', + port: '', + pathname: '/', + search: '?foo=bar', + hash: '', + }; + const actual = parseUri(uri); + expect(actual).toEqual(expected); + }); + + it('should parse a URL with a hash', () => { + const uri = 'https://example.com#foo'; + const expected = { + raw: uri, + protocol: 'https:', + nerf: '//example.com/', + host: 'example.com', + hostOnlyDomain: '//example.com/', + port: '', + pathname: '/', + search: '', + hash: '#foo', + }; + const actual = parseUri(uri); + expect(actual).toEqual(expected); + }); + + it('should parse a URL with a path', () => { + const uri = 'https://example.com/path/to/file'; + const expected = { + raw: uri, + protocol: 'https:', + nerf: '//example.com/path/to/', + host: 'example.com', + hostOnlyDomain: '//example.com/', + port: '', + pathname: '/path/to/file', + search: '', + hash: '', + }; + const actual = parseUri(uri); + expect(actual).toEqual(expected); + }); + + it('should parse a URL with a query string', () => { + const uri = 'https://example.com?foo=bar&baz=qux'; + const expected = { + raw: uri, + protocol: 'https:', + nerf: '//example.com/', + host: 'example.com', + hostOnlyDomain: '//example.com/', + port: '', + pathname: '/', + search: '?foo=bar&baz=qux', + hash: '', + }; + const actual = parseUri(uri); + expect(actual).toEqual(expected); + }); + + it('should parse a URL with a fragment identifier', () => { + const uri = 'https://example.com#foo'; + const expected = { + raw: uri, + protocol: 'https:', + nerf: '//example.com/', + host: 'example.com', + hostOnlyDomain: '//example.com/', + port: '', + pathname: '/', + search: '', + hash: '#foo', + }; + const actual = parseUri(uri); + expect(actual).toEqual(expected); + }); + + it('should parse a URL with a username and password', () => { + const uri = 'https://username:password@example.com'; + const expected = { + raw: uri, + protocol: 'https:', + nerf: '//example.com/', + host: 'example.com', + hostOnlyDomain: '//example.com/', + port: '', + pathname: '/', + search: '', + hash: '', + }; + const actual = parseUri(uri); + expect(actual).toEqual(expected); + }); + + it('should parse a URL that its an IP with port', () => { + const uri = 'https://192.168.1.1:8080'; + const expected = { + raw: uri, + protocol: 'https:', + nerf: '//192.168.1.1:8080/', + hostOnlyDomain: '//192.168.1.1:8080/', + host: '192.168.1.1', + port: '8080', + pathname: '/', + search: '', + hash: '', + }; + const actual = parseUri(uri); + expect(actual).toEqual(expected); + }); + + it('should parse a URL with port and subpaths ', () => { + const uri = 'https://example.com:8080/path/to/file'; + const expected = { + raw: uri, + protocol: 'https:', + nerf: '//example.com:8080/path/to/', + host: 'example.com', + hostOnlyDomain: '//example.com:8080/', + port: '8080', + pathname: '/path/to/file', + search: '', + hash: '', + }; + const actual = parseUri(uri); + expect(actual).toEqual(expected); + }); +}); diff --git a/network/url/url.ts b/network/url/url.ts new file mode 100644 index 0000000..94e2b2c --- /dev/null +++ b/network/url/url.ts @@ -0,0 +1,71 @@ +import nerfDart from 'nerf-dart'; + +export interface ParsedUri { + /** + * The url as string + */ + raw: string; + /** + * The protocol of the url + */ + protocol: string; + /** + * The nerf dart of the url + * @example https://example.com -> //example.com/ + * @example https://example.com:8080/path/to/file -> //example.com:8080/path/to/ + */ + nerf: string; + /** + * The host of the url + * @example https://example.com -> example.com + */ + host: string; + /** + * The host of the url with port + * @example https://example.com:8080 -> //example.com:8080/ + */ + hostOnlyDomain: string; + /** + * The port of the url + * @example https://example.com:8080 -> 8080 + */ + port: string; + /** + * The pathname of the url + * @example https://example.com/path/to/file -> /path/to/file + */ + pathname: string; + /** + * The search of the url + * @example https://example.com/path/to/file?search=query -> ?search=query + */ + search: string; + /** + * The hash of the url + * @example https://example.com/path/to/file#hash -> #hash + */ + hash: string; +} + +export function parseUri(uri: string): ParsedUri { + const parsed = new URL(uri); + return { + raw: uri, + protocol: parsed.protocol, + nerf: nerfDart(uri), + host: parsed.hostname, + hostOnlyDomain: convertToDomain(parsed), + port: parsed.port, + pathname: parsed.pathname, + search: parsed.search, + hash: parsed.hash, + }; +} + +function convertToDomain(url: URL): string { + let result = `//${url.hostname}`; + if (url.port) { + result += `:${url.port}`; + } + return `${result}/`; +} From f1bff635b04ca83ef0ddaf49066aafa996eb1923 Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Thu, 25 Jan 2024 12:34:37 +0100 Subject: [PATCH 19/33] fix: trailing slash --- network/agent/agent.spec.ts | 2 +- network/agent/agent.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/network/agent/agent.spec.ts b/network/agent/agent.spec.ts index f26f6ee..5f3429c 100644 --- a/network/agent/agent.spec.ts +++ b/network/agent/agent.spec.ts @@ -207,7 +207,7 @@ test('select correct client certificates when host has a path', () => { test('select correct client certificates when host has a path and the cert contains a path', () => { const agent = getAgent('https://foo.com/bar/baz', { clientCertificates: { - '//foo.com/bar': { + '//foo.com/bar/': { ca: 'ca', cert: 'cert', key: 'key', diff --git a/network/agent/agent.ts b/network/agent/agent.ts index 4d3996b..484255e 100644 --- a/network/agent/agent.ts +++ b/network/agent/agent.ts @@ -23,14 +23,14 @@ export function getAgent (uri: string, opts: AgentOptions) { } function getClientCertificates(uri: string, opts: AgentOptions) { - const { host, hostOnlyDomain, pathname } = parseUri(uri) + const { host, hostOnlyDomain, pathname, nerf } = parseUri(uri) if (host.endsWith(pathname)) { return opts.clientCertificates?.[host]; } - const fullPath = `${host}${pathname !== '/' ? pathname : ''}`; - return opts.clientCertificates?.[fullPath] ?? opts.clientCertificates?.[host] ?? opts.clientCertificates?.[hostOnlyDomain]; + const fullPath = `${nerf}${pathname !== '/' ? pathname : ''}`; + return opts.clientCertificates?.[fullPath] ?? opts.clientCertificates?.[nerf] ?? opts.clientCertificates?.[hostOnlyDomain]; } function getNonProxyAgent (uri: string, opts: AgentOptions) { From d7600ef486787e7d1af013358bc8c57441c17f71 Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Wed, 31 Jan 2024 17:59:07 +0100 Subject: [PATCH 20/33] fix: get each uri part correctly --- network/agent/agent.ts | 27 +++-- network/url/index.ts | 4 +- network/url/url.spec.ts | 234 ++++++++++++---------------------------- network/url/url.ts | 124 ++++++++++----------- 4 files changed, 148 insertions(+), 241 deletions(-) diff --git a/network/agent/agent.ts b/network/agent/agent.ts index 484255e..d8f6ee8 100644 --- a/network/agent/agent.ts +++ b/network/agent/agent.ts @@ -2,7 +2,7 @@ import { URL } from 'url' import HttpAgent from 'agentkeepalive' import LRU from 'lru-cache' import { getProxyAgent, ProxyAgentOptions } from '@pnpm/network.proxy-agent' -import { parseUri } from '@pnpm/network.url'; +import { getMaxParts, parseUri } from '@pnpm/network.url'; const HttpsAgent = HttpAgent.HttpsAgent @@ -22,15 +22,22 @@ export function getAgent (uri: string, opts: AgentOptions) { return getNonProxyAgent(uri, opts) } -function getClientCertificates(uri: string, opts: AgentOptions) { - const { host, hostOnlyDomain, pathname, nerf } = parseUri(uri) - - if (host.endsWith(pathname)) { - return opts.clientCertificates?.[host]; +function getClientCertificates(certs: AgentOptions['clientCertificates'], uri: string, maxParts: number) { + if (!certs) { + return {} } - - const fullPath = `${nerf}${pathname !== '/' ? pathname : ''}`; - return opts.clientCertificates?.[fullPath] ?? opts.clientCertificates?.[nerf] ?? opts.clientCertificates?.[hostOnlyDomain]; + const { nerf, withoutPort } = parseUri(uri) + if (certs[uri]) return certs[uri] + const parts = nerf.split('/') + for (let i = Math.min(parts.length, maxParts) - 1; i >= 3; i--) { + const key = `${parts.slice(0, i).join('/')}/` + if (certs[key]) return certs[key] + } + const urlWithoutPort = withoutPort + if (urlWithoutPort !== uri) { + return getClientCertificates(certs, urlWithoutPort, maxParts) + } + return {} } function getNonProxyAgent (uri: string, opts: AgentOptions) { @@ -39,7 +46,7 @@ function getNonProxyAgent (uri: string, opts: AgentOptions) { const { ca, cert, key: certKey } = { ...opts, - ...getClientCertificates(uri, opts), + ...getClientCertificates(opts.clientCertificates, uri, getMaxParts(Object.keys(opts.clientCertificates ?? {}))) } /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ diff --git a/network/url/index.ts b/network/url/index.ts index a8ea9e0..38f96f7 100644 --- a/network/url/index.ts +++ b/network/url/index.ts @@ -1,2 +1,2 @@ -export { parseUri } from './url'; -export type { ParsedUri } from './url'; +export { parseUri, getMaxParts } from "./url"; +export type { ParsedUri } from "./url"; diff --git a/network/url/url.spec.ts b/network/url/url.spec.ts index 5123fcb..3f3fa6e 100644 --- a/network/url/url.spec.ts +++ b/network/url/url.spec.ts @@ -1,173 +1,73 @@ -import { parseUri } from './url'; +import { parseUri } from "./url"; -describe('parseUri', () => { - it('should parse a simple URL', () => { - const uri = 'https://example.com'; - const expected = { - raw: uri, - protocol: 'https:', - nerf: '//example.com/', - host: 'example.com', - hostOnlyDomain: '//example.com/', - port: '', - pathname: '/', - search: '', - hash: '', - }; - const actual = parseUri(uri); - expect(actual).toEqual(expected); - }); +describe("parseUri", () => { + it("should parse a simple url", () => { + const uri = "https://example.com"; + const expected = { + raw: "https://example.com/", + parsed: new URL(uri), + nerf: "//example.com/", + host: "example.com", + hostOnlyDomain: "//example.com/", + withoutPort: "https://example.com/", + }; + const actual = parseUri(uri); + expect(actual).toEqual(expected); + }); - it('should parse a URL with a port', () => { - const uri = 'https://example.com:8080'; - const expected = { - raw: uri, - protocol: 'https:', - nerf: '//example.com:8080/', - hostOnlyDomain: '//example.com:8080/', - host: 'example.com', - port: '8080', - pathname: '/', - search: '', - hash: '', - }; - const actual = parseUri(uri); - expect(actual).toEqual(expected); - }); + it("should parse a url with a port", () => { + const uri = "https://example.com:8080"; + const expected = { + raw: "https://example.com:8080/", + parsed: new URL(uri), + nerf: "//example.com:8080/", + host: "example.com", + hostOnlyDomain: "//example.com:8080/", + withoutPort: "https://example.com/", + }; + const actual = parseUri(uri); + expect(actual).toEqual(expected); + }); - it('should parse a URL with a search', () => { - const uri = 'https://example.com?foo=bar'; - const expected = { - raw: uri, - protocol: 'https:', - nerf: '//example.com/', - host: 'example.com', - hostOnlyDomain: '//example.com/', - port: '', - pathname: '/', - search: '?foo=bar', - hash: '', - }; - const actual = parseUri(uri); - expect(actual).toEqual(expected); - }); + it("should parse a url with a path", () => { + const uri = "https://example.com/path/to/file"; + const expected = { + raw: "https://example.com/path/to/file", + parsed: new URL(uri), + nerf: "//example.com/path/to/file/", + host: "example.com", + hostOnlyDomain: "//example.com/", + withoutPort: "https://example.com/path/to/file", + }; + const actual = parseUri(uri); + expect(actual).toEqual(expected); + }); - it('should parse a URL with a hash', () => { - const uri = 'https://example.com#foo'; - const expected = { - raw: uri, - protocol: 'https:', - nerf: '//example.com/', - host: 'example.com', - hostOnlyDomain: '//example.com/', - port: '', - pathname: '/', - search: '', - hash: '#foo', - }; - const actual = parseUri(uri); - expect(actual).toEqual(expected); - }); + it("should parse a url with a query string", () => { + const uri = "https://example.com?foo=bar"; + const expected = { + raw: "https://example.com/?foo=bar", + parsed: new URL(uri), + nerf: "//example.com/", + host: "example.com", + hostOnlyDomain: "//example.com/", + withoutPort: "https://example.com/?foo=bar", + }; + const actual = parseUri(uri); + expect(actual).toEqual(expected); + }); - it('should parse a URL with a path', () => { - const uri = 'https://example.com/path/to/file'; - const expected = { - raw: uri, - protocol: 'https:', - nerf: '//example.com/path/to/', - host: 'example.com', - hostOnlyDomain: '//example.com/', - port: '', - pathname: '/path/to/file', - search: '', - hash: '', - }; - const actual = parseUri(uri); - expect(actual).toEqual(expected); - }); - - it('should parse a URL with a query string', () => { - const uri = 'https://example.com?foo=bar&baz=qux'; - const expected = { - raw: uri, - protocol: 'https:', - nerf: '//example.com/', - host: 'example.com', - hostOnlyDomain: '//example.com/', - port: '', - pathname: '/', - search: '?foo=bar&baz=qux', - hash: '', - }; - const actual = parseUri(uri); - expect(actual).toEqual(expected); - }); - - it('should parse a URL with a fragment identifier', () => { - const uri = 'https://example.com#foo'; - const expected = { - raw: uri, - protocol: 'https:', - nerf: '//example.com/', - host: 'example.com', - hostOnlyDomain: '//example.com/', - port: '', - pathname: '/', - search: '', - hash: '#foo', - }; - const actual = parseUri(uri); - expect(actual).toEqual(expected); - }); - - it('should parse a URL with a username and password', () => { - const uri = 'https://username:password@example.com'; - const expected = { - raw: uri, - protocol: 'https:', - nerf: '//example.com/', - host: 'example.com', - hostOnlyDomain: '//example.com/', - port: '', - pathname: '/', - search: '', - hash: '', - }; - const actual = parseUri(uri); - expect(actual).toEqual(expected); - }); - - it('should parse a URL that its an IP with port', () => { - const uri = 'https://192.168.1.1:8080'; - const expected = { - raw: uri, - protocol: 'https:', - nerf: '//192.168.1.1:8080/', - hostOnlyDomain: '//192.168.1.1:8080/', - host: '192.168.1.1', - port: '8080', - pathname: '/', - search: '', - hash: '', - }; - const actual = parseUri(uri); - expect(actual).toEqual(expected); - }); - - it('should parse a URL with port and subpaths ', () => { - const uri = 'https://example.com:8080/path/to/file'; - const expected = { - raw: uri, - protocol: 'https:', - nerf: '//example.com:8080/path/to/', - host: 'example.com', - hostOnlyDomain: '//example.com:8080/', - port: '8080', - pathname: '/path/to/file', - search: '', - hash: '', - }; - const actual = parseUri(uri); - expect(actual).toEqual(expected); - }); + it("should parse a url with a fragment identifier", () => { + const uri = "https://example.com#fragment"; + const expected = { + raw: "https://example.com/#fragment", + parsed: new URL(uri), + nerf: "//example.com/", + host: "example.com", + hostOnlyDomain: "//example.com/", + withoutPort: "https://example.com/#fragment", + }; + const actual = parseUri(uri); + expect(actual).toEqual(expected); + }); }); diff --git a/network/url/url.ts b/network/url/url.ts index 94e2b2c..f501238 100644 --- a/network/url/url.ts +++ b/network/url/url.ts @@ -1,71 +1,71 @@ -import nerfDart from 'nerf-dart'; +import nerfDart from "nerf-dart"; export interface ParsedUri { - /** - * The url as string - */ - raw: string; - /** - * The protocol of the url - */ - protocol: string; - /** - * The nerf dart of the url - * @example https://example.com -> //example.com/ - * @example https://example.com:8080/path/to/file -> //example.com:8080/path/to/ - */ - nerf: string; - /** - * The host of the url - * @example https://example.com -> example.com - */ - host: string; - /** - * The host of the url with port - * @example https://example.com:8080 -> //example.com:8080/ - */ - hostOnlyDomain: string; - /** - * The port of the url - * @example https://example.com:8080 -> 8080 - */ - port: string; - /** - * The pathname of the url - * @example https://example.com/path/to/file -> /path/to/file - */ - pathname: string; - /** - * The search of the url - * @example https://example.com/path/to/file?search=query -> ?search=query - */ - search: string; - /** - * The hash of the url - * @example https://example.com/path/to/file#hash -> #hash - */ - hash: string; + /** + * The url as string + */ + raw: string; + /** + * The parsed url + */ + parsed: URL; + /** + * The nerf dart of the url + * @example https://example.com -> //example.com/ + * @example https://example.com:8080/path/to/file -> //example.com:8080/path/to/ + */ + nerf: string; + /** + * The host of the url + * @example https://example.com -> example.com + */ + host: string; + /** + * The host of the url with port + * @example https://example.com:8080 -> //example.com:8080/ + */ + hostOnlyDomain: string; + /** + * The url without port + * @example https://example.com:8080/path/to/file -> https://example.com/path/to/file + */ + withoutPort: string; } export function parseUri(uri: string): ParsedUri { - const parsed = new URL(uri); - return { - raw: uri, - protocol: parsed.protocol, - nerf: nerfDart(uri), - host: parsed.hostname, - hostOnlyDomain: convertToDomain(parsed), - port: parsed.port, - pathname: parsed.pathname, - search: parsed.search, - hash: parsed.hash, - }; + const parsed = new URL(uri); + + if (!uri.endsWith("/")) { + uri += "/"; + } + + return { + raw: parsed.href, + parsed, + nerf: nerfDart(uri), + host: parsed.hostname, + hostOnlyDomain: convertToDomain(parsed), + withoutPort: removePort(parsed), + }; +} + +export function getMaxParts(uris: string[]) { + return uris.reduce((max, uri) => { + const parts = uri.split("/").length; + return parts > max ? parts : max; + }, 0); } function convertToDomain(url: URL): string { - let result = `//${url.hostname}`; - if (url.port) { - result += `:${url.port}`; - } - return `${result}/`; + let result = `//${url.hostname}`; + if (url.port) { + result += `:${url.port}`; + } + return `${result}/`; +} + +function removePort(url: URL): string { + if (url.port === "") return url.href; + url.port = ""; + return url.toString(); } From 03bea7d3185b19bc5a9cd576839b3afce979dc93 Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Wed, 31 Jan 2024 19:25:13 +0100 Subject: [PATCH 21/33] refactor: move `getXFromUri` to `url` component --- network/agent/agent.ts | 22 ++-------------------- network/url/index.ts | 2 +- network/url/url.ts | 21 +++++++++++++++++++++ 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/network/agent/agent.ts b/network/agent/agent.ts index d8f6ee8..e144b03 100644 --- a/network/agent/agent.ts +++ b/network/agent/agent.ts @@ -2,7 +2,7 @@ import { URL } from 'url' import HttpAgent from 'agentkeepalive' import LRU from 'lru-cache' import { getProxyAgent, ProxyAgentOptions } from '@pnpm/network.proxy-agent' -import { getMaxParts, parseUri } from '@pnpm/network.url'; +import { getMaxParts, getFromUri } from '@pnpm/network.url'; const HttpsAgent = HttpAgent.HttpsAgent @@ -22,31 +22,13 @@ export function getAgent (uri: string, opts: AgentOptions) { return getNonProxyAgent(uri, opts) } -function getClientCertificates(certs: AgentOptions['clientCertificates'], uri: string, maxParts: number) { - if (!certs) { - return {} - } - const { nerf, withoutPort } = parseUri(uri) - if (certs[uri]) return certs[uri] - const parts = nerf.split('/') - for (let i = Math.min(parts.length, maxParts) - 1; i >= 3; i--) { - const key = `${parts.slice(0, i).join('/')}/` - if (certs[key]) return certs[key] - } - const urlWithoutPort = withoutPort - if (urlWithoutPort !== uri) { - return getClientCertificates(certs, urlWithoutPort, maxParts) - } - return {} -} - function getNonProxyAgent (uri: string, opts: AgentOptions) { const parsedUri = new URL(uri) const isHttps = parsedUri.protocol === 'https:' const { ca, cert, key: certKey } = { ...opts, - ...getClientCertificates(opts.clientCertificates, uri, getMaxParts(Object.keys(opts.clientCertificates ?? {}))) + ...getFromUri(opts.clientCertificates, uri, getMaxParts(Object.keys(opts.clientCertificates ?? {}))) } /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ diff --git a/network/url/index.ts b/network/url/index.ts index 38f96f7..168a727 100644 --- a/network/url/index.ts +++ b/network/url/index.ts @@ -1,2 +1,2 @@ -export { parseUri, getMaxParts } from "./url"; +export { parseUri, getMaxParts, getFromUri } from "./url"; export type { ParsedUri } from "./url"; diff --git a/network/url/url.ts b/network/url/url.ts index f501238..3b96d94 100644 --- a/network/url/url.ts +++ b/network/url/url.ts @@ -56,6 +56,27 @@ export function getMaxParts(uris: string[]) { }, 0); } +export function getFromUri( + generic: { [key: string]: T } | undefined, + uri: string, + maxParts: number, +): T | undefined { + if (!generic) return undefined; + if (generic[uri]) return generic[uri]; + const { nerf, withoutPort } = parseUri(uri); + const parts = nerf.split("/"); + for (let i = Math.min(parts.length, maxParts) - 1; i >= 3; i--) { + const key = `${parts.slice(0, i).join("/")}/`; + if (generic[key]) { + return generic[key]; + } + } + if (withoutPort !== uri) { + return getFromUri(generic, withoutPort, maxParts); + } + return undefined; +} + function convertToDomain(url: URL): string { let result = `//${url.hostname}`; if (url.port) { From 25fe3cedee4eada837404cfd6b3d64379702d45a Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Thu, 1 Feb 2024 11:06:44 +0100 Subject: [PATCH 22/33] fix: disable v8 cache --- env/envs/pnpm-env/pnpm-env.bit-env.ts | 2 ++ pnpm-lock.yaml | 10 ++++++++++ workspace.jsonc | 1 + 3 files changed, 13 insertions(+) diff --git a/env/envs/pnpm-env/pnpm-env.bit-env.ts b/env/envs/pnpm-env/pnpm-env.bit-env.ts index 5df44d1..80c126c 100644 --- a/env/envs/pnpm-env/pnpm-env.bit-env.ts +++ b/env/envs/pnpm-env/pnpm-env.bit-env.ts @@ -15,7 +15,9 @@ import { ESLintLinter, EslintTask } from "@teambit/defender.eslint-linter"; import { JestTask, JestTester } from "@teambit/defender.jest-tester"; import { PrettierFormatter } from "@teambit/defender.prettier-formatter"; import { Tester } from "@teambit/tester"; +import { NativeCompileCache } from '@teambit/toolbox.performance.v8-cache'; +NativeCompileCache.uninstall(); export class PnpmEnv extends NodeEnv { /* shorthand name for the environment */ name = "pnpm-env"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 37b1ba7..11d0f09 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,6 +38,9 @@ importers: '@teambit/react.react-env': specifier: ^1.0.34 version: 1.0.34(@teambit/legacy@node_modules+@teambit+legacy)(@types/node@18.19.8) + '@teambit/toolbox.performance.v8-cache': + specifier: ^0.0.33 + version: 0.0.33 '@teambit/typescript.typescript-compiler': specifier: ^2.0.15 version: 2.0.15(@teambit/legacy@node_modules+@teambit+legacy) @@ -145,6 +148,8 @@ importers: network/proxy-agent: {} + network/url: {} + os/env/path-extender: {} os/env/path-extender-posix: {} @@ -11931,6 +11936,11 @@ packages: engines: {node: '>=12.22.0'} dev: false + /@teambit/toolbox.performance.v8-cache@0.0.33: + resolution: {integrity: sha512-Y4+aMSOvDm+4kAsNUN7GnXwGc1GyxOtoSHo8qASJDo4JwFeNn43H0oUiexBrQGbc0u0EJi+XHYdlvx/gPWiAsQ==} + engines: {node: '>=12.22.0'} + dev: false + /@teambit/typescript.typescript-compiler@2.0.10(@teambit/legacy@node_modules+@teambit+legacy): resolution: {integrity: sha512-f6sFGVcpD+dzaFqXB8405inkDySdfIW2wl8mn2kpEBOvU5tQxc4a1OHmfsyKjesX9dPfR6+97gEs/E2UplgVKg==} peerDependencies: diff --git a/workspace.jsonc b/workspace.jsonc index f7a12e6..1b9644d 100644 --- a/workspace.jsonc +++ b/workspace.jsonc @@ -49,6 +49,7 @@ "@teambit/defender.prettier-formatter": "^1.0.8", "@teambit/dependencies.modules.packages-excluder": "^1.0.8", "@teambit/react.react-env": "^1.0.34", + "@teambit/toolbox.performance.v8-cache": "^0.0.33", "@teambit/typescript.typescript-compiler": "^2.0.15", "@types/graceful-fs": "4.1.5", "@types/string.prototype.matchall": "4.0.1", From de85ccb297625ef710612a75920f6c9197af9f00 Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Thu, 1 Feb 2024 11:23:40 +0100 Subject: [PATCH 23/33] revert: disable v8 cache --- env/envs/pnpm-env/pnpm-env.bit-env.ts | 2 -- pnpm-lock.yaml | 8 -------- workspace.jsonc | 7 ++----- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/env/envs/pnpm-env/pnpm-env.bit-env.ts b/env/envs/pnpm-env/pnpm-env.bit-env.ts index 80c126c..5df44d1 100644 --- a/env/envs/pnpm-env/pnpm-env.bit-env.ts +++ b/env/envs/pnpm-env/pnpm-env.bit-env.ts @@ -15,9 +15,7 @@ import { ESLintLinter, EslintTask } from "@teambit/defender.eslint-linter"; import { JestTask, JestTester } from "@teambit/defender.jest-tester"; import { PrettierFormatter } from "@teambit/defender.prettier-formatter"; import { Tester } from "@teambit/tester"; -import { NativeCompileCache } from '@teambit/toolbox.performance.v8-cache'; -NativeCompileCache.uninstall(); export class PnpmEnv extends NodeEnv { /* shorthand name for the environment */ name = "pnpm-env"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 11d0f09..99d884d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,9 +38,6 @@ importers: '@teambit/react.react-env': specifier: ^1.0.34 version: 1.0.34(@teambit/legacy@node_modules+@teambit+legacy)(@types/node@18.19.8) - '@teambit/toolbox.performance.v8-cache': - specifier: ^0.0.33 - version: 0.0.33 '@teambit/typescript.typescript-compiler': specifier: ^2.0.15 version: 2.0.15(@teambit/legacy@node_modules+@teambit+legacy) @@ -11936,11 +11933,6 @@ packages: engines: {node: '>=12.22.0'} dev: false - /@teambit/toolbox.performance.v8-cache@0.0.33: - resolution: {integrity: sha512-Y4+aMSOvDm+4kAsNUN7GnXwGc1GyxOtoSHo8qASJDo4JwFeNn43H0oUiexBrQGbc0u0EJi+XHYdlvx/gPWiAsQ==} - engines: {node: '>=12.22.0'} - dev: false - /@teambit/typescript.typescript-compiler@2.0.10(@teambit/legacy@node_modules+@teambit+legacy): resolution: {integrity: sha512-f6sFGVcpD+dzaFqXB8405inkDySdfIW2wl8mn2kpEBOvU5tQxc4a1OHmfsyKjesX9dPfR6+97gEs/E2UplgVKg==} peerDependencies: diff --git a/workspace.jsonc b/workspace.jsonc index 1b9644d..7b2de1e 100644 --- a/workspace.jsonc +++ b/workspace.jsonc @@ -1,7 +1,7 @@ /** * this is the main configuration file of your bit workspace. * for full documentation, please see: https://bit.dev/docs/workspace/workspace-configuration - **/{ + **/ { "$schema": "https://static.bit.dev/teambit/schemas/schema.json", /** * main configuration of the Bit workspace. @@ -49,7 +49,6 @@ "@teambit/defender.prettier-formatter": "^1.0.8", "@teambit/dependencies.modules.packages-excluder": "^1.0.8", "@teambit/react.react-env": "^1.0.34", - "@teambit/toolbox.performance.v8-cache": "^0.0.33", "@teambit/typescript.typescript-compiler": "^2.0.15", "@types/graceful-fs": "4.1.5", "@types/string.prototype.matchall": "4.0.1", @@ -81,9 +80,7 @@ "teambit.workspace/variants": { "*": { "teambit.pkg/pkg": { - "packageManagerPublishArgs": [ - "--access public" - ], + "packageManagerPublishArgs": ["--access public"], "packageJson": { "name": "@pnpm/{scope}.{name}", "private": false, From 9905ec366f6e08ec147e15322620b3a998c866bd Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Thu, 1 Feb 2024 11:28:11 +0100 Subject: [PATCH 24/33] refactor: make `getMaxParts` an internal function --- network/agent/agent.ts | 4 ++-- network/url/index.ts | 2 +- network/url/url.ts | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/network/agent/agent.ts b/network/agent/agent.ts index e144b03..b2eb3ba 100644 --- a/network/agent/agent.ts +++ b/network/agent/agent.ts @@ -2,7 +2,7 @@ import { URL } from 'url' import HttpAgent from 'agentkeepalive' import LRU from 'lru-cache' import { getProxyAgent, ProxyAgentOptions } from '@pnpm/network.proxy-agent' -import { getMaxParts, getFromUri } from '@pnpm/network.url'; +import { getFromUri } from '@pnpm/network.url'; const HttpsAgent = HttpAgent.HttpsAgent @@ -28,7 +28,7 @@ function getNonProxyAgent (uri: string, opts: AgentOptions) { const { ca, cert, key: certKey } = { ...opts, - ...getFromUri(opts.clientCertificates, uri, getMaxParts(Object.keys(opts.clientCertificates ?? {}))) + ...getFromUri(opts.clientCertificates, uri) } /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ diff --git a/network/url/index.ts b/network/url/index.ts index 168a727..d2975af 100644 --- a/network/url/index.ts +++ b/network/url/index.ts @@ -1,2 +1,2 @@ -export { parseUri, getMaxParts, getFromUri } from "./url"; +export { parseUri, getFromUri } from "./url"; export type { ParsedUri } from "./url"; diff --git a/network/url/url.ts b/network/url/url.ts index 3b96d94..29bf178 100644 --- a/network/url/url.ts +++ b/network/url/url.ts @@ -49,7 +49,7 @@ export function parseUri(uri: string): ParsedUri { }; } -export function getMaxParts(uris: string[]) { +function getMaxParts(uris: string[]) { return uris.reduce((max, uri) => { const parts = uri.split("/").length; return parts > max ? parts : max; @@ -59,11 +59,11 @@ export function getMaxParts(uris: string[]) { export function getFromUri( generic: { [key: string]: T } | undefined, uri: string, - maxParts: number, ): T | undefined { if (!generic) return undefined; if (generic[uri]) return generic[uri]; const { nerf, withoutPort } = parseUri(uri); + const maxParts = getMaxParts(Object.keys(generic)); const parts = nerf.split("/"); for (let i = Math.min(parts.length, maxParts) - 1; i >= 3; i--) { const key = `${parts.slice(0, i).join("/")}/`; @@ -72,7 +72,7 @@ export function getFromUri( } } if (withoutPort !== uri) { - return getFromUri(generic, withoutPort, maxParts); + return getFromUri(generic, withoutPort); } return undefined; } From 5b5f4153b1a8d08dc30cce8ac2b6a42a49b40875 Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Thu, 1 Feb 2024 12:32:29 +0100 Subject: [PATCH 25/33] fix: install bvm manually and use system node version --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e72d95..fc13462 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: node-version: ${{ matrix.node }} cache: 'pnpm' - name: Install Bit - run: pnpm dlx @teambit/bvm install + run: pnpm add -g @teambit/bvm && bvm install --use-system-node - name: Set PATH for Unix if: runner.os != 'Windows' From ebba2d3015f70d534607bc7fe045bc7f0e0bbc80 Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Thu, 1 Feb 2024 12:50:00 +0100 Subject: [PATCH 26/33] revert: install bvm globally --- .github/workflows/ci.yml | 2 +- .vscode/settings.json | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 .vscode/settings.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc13462..7e72d95 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: node-version: ${{ matrix.node }} cache: 'pnpm' - name: Install Bit - run: pnpm add -g @teambit/bvm && bvm install --use-system-node + run: pnpm dlx @teambit/bvm install - name: Set PATH for Unix if: runner.os != 'Windows' diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..65a1965 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "editor.defaultFormatter": "esbenp.prettier-vscode" +} From 277243e2a6920d43579edacbacd38c52edf69fd7 Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Thu, 1 Feb 2024 14:37:55 +0100 Subject: [PATCH 27/33] ci: use bit v1.6.44 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e72d95..0a8be5f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: node-version: ${{ matrix.node }} cache: 'pnpm' - name: Install Bit - run: pnpm dlx @teambit/bvm install + run: pnpm dlx @teambit/bvm install 1.6.44 - name: Set PATH for Unix if: runner.os != 'Windows' From 2b08af56c290d61c2a0e391e410179961e10518f Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Thu, 1 Feb 2024 15:38:02 +0100 Subject: [PATCH 28/33] fix: install Bit v1.6.44 --- .github/workflows/ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0a8be5f..3286325 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,12 @@ jobs: with: node-version: ${{ matrix.node }} cache: 'pnpm' + - name: Install BVM + run: pnpm add -g @teambit/bvm + - name: Set Bit to nightly + run: bvm config set RELEASE_TYPE nightly - name: Install Bit - run: pnpm dlx @teambit/bvm install 1.6.44 + run: bvm install 1.6.44 - name: Set PATH for Unix if: runner.os != 'Windows' From 20131e505b7877b534cc6d262b69df07fc7b6300 Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Thu, 1 Feb 2024 15:58:11 +0100 Subject: [PATCH 29/33] chore: remove `settings.json` --- .vscode/settings.json | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 65a1965..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "editor.defaultFormatter": "esbenp.prettier-vscode" -} From 67d97e34de3059e5f034918db229577b51bda592 Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Tue, 6 Feb 2024 17:35:20 +0100 Subject: [PATCH 30/33] refactor: change component name to `pnpm.network/config` --- .bitmap | 28 ++++---- network/agent/agent.ts | 4 +- network/config/config.docs.mdx | 10 +++ network/config/config.spec.ts | 114 +++++++++++++++++++++++++++++++++ network/config/config.ts | 93 +++++++++++++++++++++++++++ network/config/index.ts | 2 + network/url/index.ts | 2 - network/url/url.docs.mdx | 10 --- network/url/url.spec.ts | 73 --------------------- network/url/url.ts | 92 -------------------------- pnpm-lock.yaml | 4 +- 11 files changed, 237 insertions(+), 195 deletions(-) create mode 100644 network/config/config.docs.mdx create mode 100644 network/config/config.spec.ts create mode 100644 network/config/config.ts create mode 100644 network/config/index.ts delete mode 100644 network/url/index.ts delete mode 100644 network/url/url.docs.mdx delete mode 100644 network/url/url.spec.ts delete mode 100644 network/url/url.ts diff --git a/.bitmap b/.bitmap index 5f71f05..86ec46c 100644 --- a/.bitmap +++ b/.bitmap @@ -35,6 +35,20 @@ } } }, + "config": { + "name": "config", + "scope": "", + "version": "", + "defaultScope": "pnpm.network", + "mainFile": "index.ts", + "rootDir": "network/config", + "config": { + "pnpm.env/envs/pnpm-env": {}, + "teambit.envs/envs": { + "env": "pnpm.env/envs/pnpm-env" + } + } + }, "env-replace": { "name": "env-replace", "scope": "pnpm.config", @@ -161,19 +175,5 @@ } } }, - "url": { - "name": "url", - "scope": "", - "version": "", - "defaultScope": "pnpm.network", - "mainFile": "index.ts", - "rootDir": "network/url", - "config": { - "pnpm.env/envs/pnpm-env": {}, - "teambit.envs/envs": { - "env": "pnpm.env/envs/pnpm-env" - } - } - }, "$schema-version": "17.0.0" } \ No newline at end of file diff --git a/network/agent/agent.ts b/network/agent/agent.ts index b2eb3ba..091d0ce 100644 --- a/network/agent/agent.ts +++ b/network/agent/agent.ts @@ -2,7 +2,7 @@ import { URL } from 'url' import HttpAgent from 'agentkeepalive' import LRU from 'lru-cache' import { getProxyAgent, ProxyAgentOptions } from '@pnpm/network.proxy-agent' -import { getFromUri } from '@pnpm/network.url'; +import { pickSettingByUrl } from '@pnpm/network.config'; const HttpsAgent = HttpAgent.HttpsAgent @@ -28,7 +28,7 @@ function getNonProxyAgent (uri: string, opts: AgentOptions) { const { ca, cert, key: certKey } = { ...opts, - ...getFromUri(opts.clientCertificates, uri) + ...pickSettingByUrl(opts.clientCertificates, uri) } /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ diff --git a/network/config/config.docs.mdx b/network/config/config.docs.mdx new file mode 100644 index 0000000..63e7241 --- /dev/null +++ b/network/config/config.docs.mdx @@ -0,0 +1,10 @@ +--- +labels: ['Config', 'module'] +description: 'A Config module.' +--- + +A config module. + +```ts +config(); +``` diff --git a/network/config/config.spec.ts b/network/config/config.spec.ts new file mode 100644 index 0000000..4823886 --- /dev/null +++ b/network/config/config.spec.ts @@ -0,0 +1,114 @@ +import { parseUri, pickSettingByUrl } from './config'; + +describe('parseUri', () => { + it('should parse a simple config', () => { + const uri = 'https://example.com'; + const expected = { + raw: 'https://example.com/', + parsed: new URL(uri), + nerf: '//example.com/', + host: 'example.com', + hostOnlyDomain: '//example.com/', + withoutPort: 'https://example.com/', + }; + const actual = parseUri(uri); + expect(actual).toEqual(expected); + }); + + it('should parse a config with a port', () => { + const uri = 'https://example.com:8080'; + const expected = { + raw: 'https://example.com:8080/', + parsed: new URL(uri), + nerf: '//example.com:8080/', + host: 'example.com', + hostOnlyDomain: '//example.com:8080/', + withoutPort: 'https://example.com/', + }; + const actual = parseUri(uri); + expect(actual).toEqual(expected); + }); + + it('should parse a config with a path', () => { + const uri = 'https://example.com/path/to/file'; + const expected = { + raw: 'https://example.com/path/to/file', + parsed: new URL(uri), + nerf: '//example.com/path/to/file/', + host: 'example.com', + hostOnlyDomain: '//example.com/', + withoutPort: 'https://example.com/path/to/file', + }; + const actual = parseUri(uri); + expect(actual).toEqual(expected); + }); + + it('should parse a config with a query string', () => { + const uri = 'https://example.com?foo=bar'; + const expected = { + raw: 'https://example.com/?foo=bar', + parsed: new URL(uri), + nerf: '//example.com/', + host: 'example.com', + hostOnlyDomain: '//example.com/', + withoutPort: 'https://example.com/?foo=bar', + }; + const actual = parseUri(uri); + expect(actual).toEqual(expected); + }); + + it('should parse a config with a fragment identifier', () => { + const uri = 'https://example.com#fragment'; + const expected = { + raw: 'https://example.com/#fragment', + parsed: new URL(uri), + nerf: '//example.com/', + host: 'example.com', + hostOnlyDomain: '//example.com/', + withoutPort: 'https://example.com/#fragment', + }; + const actual = parseUri(uri); + expect(actual).toEqual(expected); + }); +}); + +describe('pickSettingByUrl', () => { + test('should return undefined if generic object is undefined', () => { + expect(pickSettingByUrl(undefined, 'https://example.com')).toBeUndefined(); + }); + + test('should return the exact match from the generic object', () => { + const settings = { 'https://example.com/': 'ExampleSetting' }; + expect(pickSettingByUrl(settings, 'https://example.com')).toBe( + 'ExampleSetting' + ); + }); + + test('should return a match using nerf dart', () => { + const settings = { '//example.com/': 'NerfDartSetting' }; + expect( + pickSettingByUrl(settings, 'https://example.com/path/to/resource') + ).toBe('NerfDartSetting'); + }); + + test('should return a match using withoutPort', () => { + const settings = { + 'https://example.com/path/to/resource/': 'WithoutPortSetting', + }; + expect( + pickSettingByUrl(settings, 'https://example.com:8080/path/to/resource') + ).toBe('WithoutPortSetting'); + }); + + test('should return undefined if no match is found', () => { + const settings = { 'https://example.com/': 'ExampleSetting' }; + expect(pickSettingByUrl(settings, 'https://nomatch.com')).toBeUndefined(); + }); + + test('should recursively match using withoutPort', () => { + const settings = { 'https://example.com/': 'RecursiveSetting' }; + expect(pickSettingByUrl(settings, 'https://example.com:8080')).toBe( + 'RecursiveSetting' + ); + }); +}); diff --git a/network/config/config.ts b/network/config/config.ts new file mode 100644 index 0000000..e16d972 --- /dev/null +++ b/network/config/config.ts @@ -0,0 +1,93 @@ +import nerfDart from 'nerf-dart'; + +export interface ParsedUri { + /** + * The config as string + */ + raw: string; + /** + * The parsed config + */ + parsed: URL; + /** + * The nerf dart of the config + * @example https://example.com -> //example.com/ + * @example https://example.com:8080/path/to/file -> //example.com:8080/path/to/ + */ + nerf: string; + /** + * The host of the config + * @example https://example.com -> example.com + */ + host: string; + /** + * The host of the config with port + * @example https://example.com:8080 -> //example.com:8080/ + */ + hostOnlyDomain: string; + /** + * The config without port + * @example https://example.com:8080/path/to/file -> https://example.com/path/to/file + */ + withoutPort: string; +} + +export function parseUri(uri: string): ParsedUri { + const parsed = new URL(uri); + + if (!uri.endsWith('/')) { + uri += '/'; + } + + return { + raw: parsed.href, + parsed, + nerf: nerfDart(uri), + host: parsed.hostname, + hostOnlyDomain: convertToDomain(parsed), + withoutPort: removePort(parsed), + }; +} + +function getMaxParts(uris: string[]) { + return uris.reduce((max, uri) => { + const parts = uri.split('/').length; + return parts > max ? parts : max; + }, 0); +} + +export function pickSettingByUrl( + generic: { [key: string]: T } | undefined, + uri: string +): T | undefined { + if (!generic) return undefined; + if (generic[uri]) return generic[uri]; + const { nerf, withoutPort } = parseUri(uri); + const maxParts = getMaxParts(Object.keys(generic)); + const parts = nerf.split('/'); + for (let i = Math.min(parts.length, maxParts) - 1; i >= 3; i--) { + const key = `${parts.slice(0, i).join('/')}/`; + if (generic[key]) { + return generic[key]; + } + } + if (withoutPort !== uri) { + return pickSettingByUrl(generic, withoutPort); + } + return undefined; +} + +function convertToDomain(config: URL): string { + let result = `//${config.hostname}`; + if (config.port) { + result += `:${config.port}`; + } + return `${result}/`; +} + +function removePort(config: URL): string { + if (config.port === '') return config.href; + config.port = ''; + const res = config.toString(); + return res.endsWith('/') ? res : `${res}/`; +} diff --git a/network/config/index.ts b/network/config/index.ts new file mode 100644 index 0000000..d609726 --- /dev/null +++ b/network/config/index.ts @@ -0,0 +1,2 @@ +export { parseUri, pickSettingByUrl } from './config'; +export type { ParsedUri } from './config'; diff --git a/network/url/index.ts b/network/url/index.ts deleted file mode 100644 index d2975af..0000000 --- a/network/url/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { parseUri, getFromUri } from "./url"; -export type { ParsedUri } from "./url"; diff --git a/network/url/url.docs.mdx b/network/url/url.docs.mdx deleted file mode 100644 index cc25a35..0000000 --- a/network/url/url.docs.mdx +++ /dev/null @@ -1,10 +0,0 @@ ---- -labels: ['Url', 'module'] -description: 'A Url module.' ---- - -A url module. - -```ts -url(); -``` diff --git a/network/url/url.spec.ts b/network/url/url.spec.ts deleted file mode 100644 index 3f3fa6e..0000000 --- a/network/url/url.spec.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { parseUri } from "./url"; - -describe("parseUri", () => { - it("should parse a simple url", () => { - const uri = "https://example.com"; - const expected = { - raw: "https://example.com/", - parsed: new URL(uri), - nerf: "//example.com/", - host: "example.com", - hostOnlyDomain: "//example.com/", - withoutPort: "https://example.com/", - }; - const actual = parseUri(uri); - expect(actual).toEqual(expected); - }); - - it("should parse a url with a port", () => { - const uri = "https://example.com:8080"; - const expected = { - raw: "https://example.com:8080/", - parsed: new URL(uri), - nerf: "//example.com:8080/", - host: "example.com", - hostOnlyDomain: "//example.com:8080/", - withoutPort: "https://example.com/", - }; - const actual = parseUri(uri); - expect(actual).toEqual(expected); - }); - - it("should parse a url with a path", () => { - const uri = "https://example.com/path/to/file"; - const expected = { - raw: "https://example.com/path/to/file", - parsed: new URL(uri), - nerf: "//example.com/path/to/file/", - host: "example.com", - hostOnlyDomain: "//example.com/", - withoutPort: "https://example.com/path/to/file", - }; - const actual = parseUri(uri); - expect(actual).toEqual(expected); - }); - - it("should parse a url with a query string", () => { - const uri = "https://example.com?foo=bar"; - const expected = { - raw: "https://example.com/?foo=bar", - parsed: new URL(uri), - nerf: "//example.com/", - host: "example.com", - hostOnlyDomain: "//example.com/", - withoutPort: "https://example.com/?foo=bar", - }; - const actual = parseUri(uri); - expect(actual).toEqual(expected); - }); - - it("should parse a url with a fragment identifier", () => { - const uri = "https://example.com#fragment"; - const expected = { - raw: "https://example.com/#fragment", - parsed: new URL(uri), - nerf: "//example.com/", - host: "example.com", - hostOnlyDomain: "//example.com/", - withoutPort: "https://example.com/#fragment", - }; - const actual = parseUri(uri); - expect(actual).toEqual(expected); - }); -}); diff --git a/network/url/url.ts b/network/url/url.ts deleted file mode 100644 index 29bf178..0000000 --- a/network/url/url.ts +++ /dev/null @@ -1,92 +0,0 @@ -import nerfDart from "nerf-dart"; - -export interface ParsedUri { - /** - * The url as string - */ - raw: string; - /** - * The parsed url - */ - parsed: URL; - /** - * The nerf dart of the url - * @example https://example.com -> //example.com/ - * @example https://example.com:8080/path/to/file -> //example.com:8080/path/to/ - */ - nerf: string; - /** - * The host of the url - * @example https://example.com -> example.com - */ - host: string; - /** - * The host of the url with port - * @example https://example.com:8080 -> //example.com:8080/ - */ - hostOnlyDomain: string; - /** - * The url without port - * @example https://example.com:8080/path/to/file -> https://example.com/path/to/file - */ - withoutPort: string; -} - -export function parseUri(uri: string): ParsedUri { - const parsed = new URL(uri); - - if (!uri.endsWith("/")) { - uri += "/"; - } - - return { - raw: parsed.href, - parsed, - nerf: nerfDart(uri), - host: parsed.hostname, - hostOnlyDomain: convertToDomain(parsed), - withoutPort: removePort(parsed), - }; -} - -function getMaxParts(uris: string[]) { - return uris.reduce((max, uri) => { - const parts = uri.split("/").length; - return parts > max ? parts : max; - }, 0); -} - -export function getFromUri( - generic: { [key: string]: T } | undefined, - uri: string, -): T | undefined { - if (!generic) return undefined; - if (generic[uri]) return generic[uri]; - const { nerf, withoutPort } = parseUri(uri); - const maxParts = getMaxParts(Object.keys(generic)); - const parts = nerf.split("/"); - for (let i = Math.min(parts.length, maxParts) - 1; i >= 3; i--) { - const key = `${parts.slice(0, i).join("/")}/`; - if (generic[key]) { - return generic[key]; - } - } - if (withoutPort !== uri) { - return getFromUri(generic, withoutPort); - } - return undefined; -} - -function convertToDomain(url: URL): string { - let result = `//${url.hostname}`; - if (url.port) { - result += `:${url.port}`; - } - return `${result}/`; -} - -function removePort(url: URL): string { - if (url.port === "") return url.href; - url.port = ""; - return url.toString(); -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 99d884d..99a231d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,9 +143,9 @@ importers: network/ca-file: {} - network/proxy-agent: {} + network/config: {} - network/url: {} + network/proxy-agent: {} os/env/path-extender: {} From b2ae53fb41b11507f73b90a8e58fa5638b47a76e Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Tue, 6 Feb 2024 17:49:18 +0100 Subject: [PATCH 31/33] fix: replate `\r\n` with `\n` to match UNIX style --- network/ca-file/ca-file.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/network/ca-file/ca-file.ts b/network/ca-file/ca-file.ts index b9771ac..bb8fae7 100644 --- a/network/ca-file/ca-file.ts +++ b/network/ca-file/ca-file.ts @@ -2,7 +2,9 @@ import fs from 'graceful-fs' export function readCAFileSync (filePath: string): string[] | undefined { try { - const contents = fs.readFileSync(filePath, 'utf8') + let contents = fs.readFileSync(filePath, 'utf8') + // Normalize line endings to Unix-style + contents = contents.replace(/\r\n/g, '\n'); const delim = '-----END CERTIFICATE-----' const output = contents .split(delim) From 78f612d2dd6cbdc310b2083b07e0b7780274abea Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Tue, 6 Feb 2024 18:09:35 +0100 Subject: [PATCH 32/33] refactor: remove unused functions --- network/config/config.spec.ts | 74 +---------------------------------- network/config/config.ts | 63 +++-------------------------- network/config/index.ts | 3 +- 3 files changed, 7 insertions(+), 133 deletions(-) diff --git a/network/config/config.spec.ts b/network/config/config.spec.ts index 4823886..b002917 100644 --- a/network/config/config.spec.ts +++ b/network/config/config.spec.ts @@ -1,76 +1,4 @@ -import { parseUri, pickSettingByUrl } from './config'; - -describe('parseUri', () => { - it('should parse a simple config', () => { - const uri = 'https://example.com'; - const expected = { - raw: 'https://example.com/', - parsed: new URL(uri), - nerf: '//example.com/', - host: 'example.com', - hostOnlyDomain: '//example.com/', - withoutPort: 'https://example.com/', - }; - const actual = parseUri(uri); - expect(actual).toEqual(expected); - }); - - it('should parse a config with a port', () => { - const uri = 'https://example.com:8080'; - const expected = { - raw: 'https://example.com:8080/', - parsed: new URL(uri), - nerf: '//example.com:8080/', - host: 'example.com', - hostOnlyDomain: '//example.com:8080/', - withoutPort: 'https://example.com/', - }; - const actual = parseUri(uri); - expect(actual).toEqual(expected); - }); - - it('should parse a config with a path', () => { - const uri = 'https://example.com/path/to/file'; - const expected = { - raw: 'https://example.com/path/to/file', - parsed: new URL(uri), - nerf: '//example.com/path/to/file/', - host: 'example.com', - hostOnlyDomain: '//example.com/', - withoutPort: 'https://example.com/path/to/file', - }; - const actual = parseUri(uri); - expect(actual).toEqual(expected); - }); - - it('should parse a config with a query string', () => { - const uri = 'https://example.com?foo=bar'; - const expected = { - raw: 'https://example.com/?foo=bar', - parsed: new URL(uri), - nerf: '//example.com/', - host: 'example.com', - hostOnlyDomain: '//example.com/', - withoutPort: 'https://example.com/?foo=bar', - }; - const actual = parseUri(uri); - expect(actual).toEqual(expected); - }); - - it('should parse a config with a fragment identifier', () => { - const uri = 'https://example.com#fragment'; - const expected = { - raw: 'https://example.com/#fragment', - parsed: new URL(uri), - nerf: '//example.com/', - host: 'example.com', - hostOnlyDomain: '//example.com/', - withoutPort: 'https://example.com/#fragment', - }; - const actual = parseUri(uri); - expect(actual).toEqual(expected); - }); -}); +import { pickSettingByUrl } from './config'; describe('pickSettingByUrl', () => { test('should return undefined if generic object is undefined', () => { diff --git a/network/config/config.ts b/network/config/config.ts index e16d972..0d6ea8e 100644 --- a/network/config/config.ts +++ b/network/config/config.ts @@ -1,54 +1,5 @@ import nerfDart from 'nerf-dart'; -export interface ParsedUri { - /** - * The config as string - */ - raw: string; - /** - * The parsed config - */ - parsed: URL; - /** - * The nerf dart of the config - * @example https://example.com -> //example.com/ - * @example https://example.com:8080/path/to/file -> //example.com:8080/path/to/ - */ - nerf: string; - /** - * The host of the config - * @example https://example.com -> example.com - */ - host: string; - /** - * The host of the config with port - * @example https://example.com:8080 -> //example.com:8080/ - */ - hostOnlyDomain: string; - /** - * The config without port - * @example https://example.com:8080/path/to/file -> https://example.com/path/to/file - */ - withoutPort: string; -} - -export function parseUri(uri: string): ParsedUri { - const parsed = new URL(uri); - - if (!uri.endsWith('/')) { - uri += '/'; - } - - return { - raw: parsed.href, - parsed, - nerf: nerfDart(uri), - host: parsed.hostname, - hostOnlyDomain: convertToDomain(parsed), - withoutPort: removePort(parsed), - }; -} - function getMaxParts(uris: string[]) { return uris.reduce((max, uri) => { const parts = uri.split('/').length; @@ -62,7 +13,11 @@ export function pickSettingByUrl( ): T | undefined { if (!generic) return undefined; if (generic[uri]) return generic[uri]; - const { nerf, withoutPort } = parseUri(uri); + /* const { nerf, withoutPort } = parseUri(uri); */ + const nerf = nerfDart(uri); + const withoutPort = removePort(new URL(uri)); + if (generic[nerf]) return generic[nerf]; + if (generic[withoutPort]) return generic[withoutPort]; const maxParts = getMaxParts(Object.keys(generic)); const parts = nerf.split('/'); for (let i = Math.min(parts.length, maxParts) - 1; i >= 3; i--) { @@ -77,14 +32,6 @@ export function pickSettingByUrl( return undefined; } -function convertToDomain(config: URL): string { - let result = `//${config.hostname}`; - if (config.port) { - result += `:${config.port}`; - } - return `${result}/`; -} - function removePort(config: URL): string { if (config.port === '') return config.href; config.port = ''; diff --git a/network/config/index.ts b/network/config/index.ts index d609726..fda18dd 100644 --- a/network/config/index.ts +++ b/network/config/index.ts @@ -1,2 +1 @@ -export { parseUri, pickSettingByUrl } from './config'; -export type { ParsedUri } from './config'; +export { pickSettingByUrl } from './config'; From 19e788ab8509b2312556a30b3f52d86d48df5c96 Mon Sep 17 00:00:00 2001 From: Ignacio Aldama Vicente Date: Tue, 6 Feb 2024 20:18:44 +0100 Subject: [PATCH 33/33] chore: fix TS errors --- builder/policy/policy.spec.ts | 30 ++++++++++++------- config/env-replace/env-replace.ts | 2 +- env/envs/pnpm-env/types/nerf.d.ts | 3 ++ network/ca-file/ca-file.ts | 2 +- network/proxy-agent/proxy-agent.spec.ts | 2 +- .../proxy-agent.strict-ssl.spec.ts | 2 +- .../path-extender-windows.ts | 11 ++++--- os/env/path-extender/path-extender.ts | 4 +-- pnpm-lock.yaml | 20 ++++++++++++- workspace.jsonc | 8 +++-- 10 files changed, 60 insertions(+), 24 deletions(-) create mode 100644 env/envs/pnpm-env/types/nerf.d.ts diff --git a/builder/policy/policy.spec.ts b/builder/policy/policy.spec.ts index 2f0530a..a2177b6 100644 --- a/builder/policy/policy.spec.ts +++ b/builder/policy/policy.spec.ts @@ -5,25 +5,31 @@ it('should neverBuiltDependencies', () => { const allowBuild = createAllowBuildFunction({ neverBuiltDependencies: ['foo'], }) - expect(allowBuild('foo')).toBeFalsy() - expect(allowBuild('bar')).toBeTruthy() + if (allowBuild) { + expect(allowBuild('foo')).toBeFalsy() + expect(allowBuild('bar')).toBeTruthy() + } }) it('should onlyBuiltDependencies', () => { const allowBuild = createAllowBuildFunction({ onlyBuiltDependencies: ['foo'], }) - expect(allowBuild('foo')).toBeTruthy() - expect(allowBuild('bar')).toBeFalsy() + if (allowBuild) { + expect(allowBuild('foo')).toBeTruthy() + expect(allowBuild('bar')).toBeFalsy() + } }) it('should onlyBuiltDependencies set via a file', () => { const allowBuild = createAllowBuildFunction({ onlyBuiltDependenciesFile: path.join(__dirname, 'onlyBuild.json'), }) - expect(allowBuild('zoo')).toBeTruthy() - expect(allowBuild('qar')).toBeTruthy() - expect(allowBuild('bar')).toBeFalsy() + if (allowBuild) { + expect(allowBuild('zoo')).toBeTruthy() + expect(allowBuild('qar')).toBeTruthy() + expect(allowBuild('bar')).toBeFalsy() + } }) it('should onlyBuiltDependencies set via a file and config', () => { @@ -31,10 +37,12 @@ it('should onlyBuiltDependencies set via a file and config', () => { onlyBuiltDependencies: ['bar'], onlyBuiltDependenciesFile: path.join(__dirname, 'onlyBuild.json'), }) - expect(allowBuild('zoo')).toBeTruthy() - expect(allowBuild('qar')).toBeTruthy() - expect(allowBuild('bar')).toBeTruthy() - expect(allowBuild('esbuild')).toBeFalsy() + if (allowBuild) { + expect(allowBuild('zoo')).toBeTruthy() + expect(allowBuild('qar')).toBeTruthy() + expect(allowBuild('bar')).toBeTruthy() + expect(allowBuild('esbuild')).toBeFalsy() + } }) it('should return undefined if no policy is set', () => { diff --git a/config/env-replace/env-replace.ts b/config/env-replace/env-replace.ts index cfe597b..d4074cc 100644 --- a/config/env-replace/env-replace.ts +++ b/config/env-replace/env-replace.ts @@ -17,7 +17,7 @@ function replaceEnvMatch (env: NodeJS.ProcessEnv, orig: string, escape: string, const ENV_VALUE = /([^:-]+)(:?)-(.+)/ -function getEnvValue (env: NodeJS.ProcessEnv, name: string): string { +function getEnvValue (env: NodeJS.ProcessEnv, name: string): string | undefined { const matched = name.match(ENV_VALUE) if (!matched) return env[name] const [, variableName, colon, fallback] = matched diff --git a/env/envs/pnpm-env/types/nerf.d.ts b/env/envs/pnpm-env/types/nerf.d.ts new file mode 100644 index 0000000..992a0b4 --- /dev/null +++ b/env/envs/pnpm-env/types/nerf.d.ts @@ -0,0 +1,3 @@ +declare module 'nerf-dart' { + export default function (uri: string): string; +} diff --git a/network/ca-file/ca-file.ts b/network/ca-file/ca-file.ts index bb8fae7..c057d93 100644 --- a/network/ca-file/ca-file.ts +++ b/network/ca-file/ca-file.ts @@ -11,7 +11,7 @@ export function readCAFileSync (filePath: string): string[] | undefined { .filter((ca) => Boolean(ca.trim())) .map((ca) => `${ca.trimLeft()}${delim}`) return output - } catch (err) { + } catch (err: any) { // eslint-disable-line if (err.code === 'ENOENT') return undefined throw err } diff --git a/network/proxy-agent/proxy-agent.spec.ts b/network/proxy-agent/proxy-agent.spec.ts index 853d384..b8ae195 100644 --- a/network/proxy-agent/proxy-agent.spec.ts +++ b/network/proxy-agent/proxy-agent.spec.ts @@ -56,7 +56,7 @@ test('a socks proxy', () => { } const agent = getProxyAgent('https://foo.com/bar', opts) expect(agent instanceof SocksProxyAgent).toBeTruthy() - expect(agent['proxy']).toEqual({ + expect((agent instanceof SocksProxyAgent)['proxy']).toEqual({ host: 'my.proxy', port: 1234, type: 5, diff --git a/network/proxy-agent/proxy-agent.strict-ssl.spec.ts b/network/proxy-agent/proxy-agent.strict-ssl.spec.ts index 7a08b13..935a6e7 100644 --- a/network/proxy-agent/proxy-agent.strict-ssl.spec.ts +++ b/network/proxy-agent/proxy-agent.strict-ssl.spec.ts @@ -3,7 +3,7 @@ import { getProxyAgent } from './proxy-agent' import Proxy from 'proxy' describe('untrusted certificate', () => { - let proxy: Proxy + let proxy: any let proxyPort: number beforeAll((done) => { // setup HTTP proxy server diff --git a/os/env/path-extender-windows/path-extender-windows.ts b/os/env/path-extender-windows/path-extender-windows.ts index ea46293..0cd60c3 100644 --- a/os/env/path-extender-windows/path-extender-windows.ts +++ b/os/env/path-extender-windows/path-extender-windows.ts @@ -30,7 +30,7 @@ export interface AddDirToWindowsEnvPathOpts { export interface EnvVariableChange { variable: string, action: EnvVariableChangeAction - oldValue: string, + oldValue: string | undefined, newValue: string, } @@ -41,7 +41,10 @@ export async function addDirToWindowsEnvPath (dir: string, opts?: AddDirToWindow // Otherwise, the non-ascii characters in the environment variables will become garbled characters. const chcpResult = await execa('chcp') const cpMatch = /\d+/.exec(chcpResult.stdout) ?? [] - const cpBak = parseInt(cpMatch[0]) + if (cpMatch.length === 0) { + throw new PnpmError('CHCP', `exec chcp failed: ${chcpResult.stderr}`) + } + const cpBak = parseInt(cpMatch[0] as string) if (chcpResult.failed || !(cpBak > 0)) { throw new PnpmError('CHCP', `exec chcp failed: ${cpBak}, ${chcpResult.stderr}`) } @@ -64,7 +67,7 @@ async function _addDirToWindowsEnvPath (dir: string, opts: AddDirToWindowsEnvPat if (opts.proxyVarName) { changes.push(await updateEnvVariable(registryOutput, opts.proxyVarName, addedDir, { expandableString: false, - overwrite: opts.overwriteProxyVar, + overwrite: opts.overwriteProxyVar ?? false })) changes.push(await addToPath(registryOutput, `%${opts.proxyVarName}%`, opts.position)) } else { @@ -91,7 +94,7 @@ async function updateEnvVariable ( return { variable: name, action: 'skipped', oldValue: currentValue, newValue: value } } else { await setEnvVarInRegistry(name, value, { expandableString: opts.expandableString }) - return { variable: name, action: 'updated', oldValue: currentValue, newValue: value } + return { variable: name, action: 'updated', oldValue: currentValue as string, newValue: value } } } diff --git a/os/env/path-extender/path-extender.ts b/os/env/path-extender/path-extender.ts index 4d495c4..9978809 100644 --- a/os/env/path-extender/path-extender.ts +++ b/os/env/path-extender/path-extender.ts @@ -26,8 +26,8 @@ export async function addDirToEnvPath(dir: string, opts: AddDirToEnvPathOpts): P } export function renderWindowsReport (changedEnvVariables: PathExtenderWindowsReport): PathExtenderReport { - const oldSettings = [] - const newSettings = [] + const oldSettings: string[] = [] + const newSettings: string[] = [] for (const changedEnvVariable of changedEnvVariables) { if (changedEnvVariable.oldValue) { oldSettings.push(`${changedEnvVariable.variable}=${changedEnvVariable.oldValue}`) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 99a231d..118684c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -105,6 +105,12 @@ importers: '@types/node': specifier: '>=18.11.9 <19.0.0' version: 18.19.8 + '@types/node-fetch': + specifier: ^2.6.11 + version: 2.6.11 + '@types/proxy': + specifier: ^1.0.4 + version: 1.0.4 '@types/react': specifier: ^17.0.8 version: 17.0.75 @@ -12902,6 +12908,13 @@ packages: resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} dev: false + /@types/node-fetch@2.6.11: + resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==} + dependencies: + '@types/node': 18.19.8 + form-data: 4.0.0 + dev: true + /@types/node-forge@1.3.11: resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} dependencies: @@ -12928,6 +12941,12 @@ packages: /@types/prop-types@15.7.11: resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} + /@types/proxy@1.0.4: + resolution: {integrity: sha512-KnYy7hpp3wXQ2U0ExvCMF9BvWZ6h2aI0XWb8g705mn26Zmn2HO/eLRi6UuhHELA6MNJtYtiZYWv38gobJN0xXQ==} + dependencies: + '@types/node': 18.19.8 + dev: true + /@types/qs@6.9.11: resolution: {integrity: sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==} dev: false @@ -18784,7 +18803,6 @@ packages: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 - dev: false /format@0.2.2: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} diff --git a/workspace.jsonc b/workspace.jsonc index 7b2de1e..a495edc 100644 --- a/workspace.jsonc +++ b/workspace.jsonc @@ -1,7 +1,7 @@ /** * this is the main configuration file of your bit workspace. * for full documentation, please see: https://bit.dev/docs/workspace/workspace-configuration - **/ { + **/{ "$schema": "https://static.bit.dev/teambit/schemas/schema.json", /** * main configuration of the Bit workspace. @@ -51,6 +51,8 @@ "@teambit/react.react-env": "^1.0.34", "@teambit/typescript.typescript-compiler": "^2.0.15", "@types/graceful-fs": "4.1.5", + "@types/node-fetch": "^2.6.11", + "@types/proxy": "^1.0.4", "@types/string.prototype.matchall": "4.0.1", "agentkeepalive": "4.2.1", "ci-info": "^3.9.0", @@ -80,7 +82,9 @@ "teambit.workspace/variants": { "*": { "teambit.pkg/pkg": { - "packageManagerPublishArgs": ["--access public"], + "packageManagerPublishArgs": [ + "--access public" + ], "packageJson": { "name": "@pnpm/{scope}.{name}", "private": false,