diff --git a/.eslintrc.js b/.eslintrc.js index e24396d3c7ab13..9d91c652b15d91 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -6,6 +6,10 @@ module.exports = { extends: [ 'airbnb-typescript/base', // https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin/src/configs + + 'plugin:import/errors', + 'plugin:import/warnings', + 'plugin:import/typescript', 'plugin:@typescript-eslint/eslint-recommended', 'plugin:@typescript-eslint/recommended', 'plugin:@typescript-eslint/recommended-requiring-type-checking', @@ -56,6 +60,10 @@ module.exports = { }, ], // disable until proper interfaced api }, + settings: { + // https://github.com/benmosher/eslint-plugin-import/issues/1618 + 'import/internal-regex': '^type\\-fest$', + }, overrides: [ { files: ['**/*.spec.ts'], diff --git a/lib/config/presets/github.ts b/lib/config/presets/github.ts index 175c7dbb82376c..2ac1833f5780db 100644 --- a/lib/config/presets/github.ts +++ b/lib/config/presets/github.ts @@ -11,8 +11,8 @@ async function fetchJSONFile(repo: string, fileName: string): Promise { ? 'application/vnd.github.machine-man-preview+json' : 'application/vnd.github.v3+json', }, - json: true, - hostType: 'github', + responseType: 'json', + context: { hostType: 'github' }, }; let res: { body: { content: string } }; try { diff --git a/lib/datasource/cdnjs/index.ts b/lib/datasource/cdnjs/index.ts index 9b44c4ef24ac47..0493ad2ddd6584 100644 --- a/lib/datasource/cdnjs/index.ts +++ b/lib/datasource/cdnjs/index.ts @@ -36,7 +36,7 @@ export async function getDigest( const assetName = lookupName.replace(`${library}/`, ''); let res = null; try { - res = await got(url, { hostType: id, json: true }); + res = await got(url, { context: { hostType: id }, responseType: 'json' }); } catch (e) /* istanbul ignore next */ { return null; } @@ -70,7 +70,10 @@ export async function getPkgReleases({ const url = depUrl(library); try { - const res = await got(url, { hostType: id, json: true }); + const res = await got(url, { + context: { hostType: id }, + responseType: 'json', + }); const cdnjsResp: CdnjsResponse = res.body; diff --git a/lib/datasource/crate/index.ts b/lib/datasource/crate/index.ts index 8dd6806bd5983d..16aabe346ba973 100644 --- a/lib/datasource/crate/index.ts +++ b/lib/datasource/crate/index.ts @@ -46,7 +46,7 @@ export async function getPkgReleases({ const crateUrl = baseUrl + path; try { let res: any = await got(crateUrl, { - hostType: id, + context: { hostType: id }, }); if (!res || !res.body) { logger.warn( diff --git a/lib/datasource/dart/index.ts b/lib/datasource/dart/index.ts index 7c571066ad1cf0..179d505a749598 100644 --- a/lib/datasource/dart/index.ts +++ b/lib/datasource/dart/index.ts @@ -23,8 +23,8 @@ export async function getPkgReleases({ } = null; try { raw = await got(pkgUrl, { - hostType: id, - json: true, + responseType: 'json', + context: { hostType: id }, }); } catch (err) { if (err.statusCode === 404 || err.code === 'ENOTFOUND') { diff --git a/lib/datasource/docker/__snapshots__/index.spec.ts.snap b/lib/datasource/docker/__snapshots__/index.spec.ts.snap index 88b83ee46b3fe7..7c778614480b94 100644 --- a/lib/datasource/docker/__snapshots__/index.spec.ts.snap +++ b/lib/datasource/docker/__snapshots__/index.spec.ts.snap @@ -15,7 +15,7 @@ exports[`api/docker getPkgReleases adds library/ prefix for Docker Hub (explicit "headers": Object { "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", }, - "json": true, + "responseType": "json", }, ], Array [ @@ -24,7 +24,7 @@ exports[`api/docker getPkgReleases adds library/ prefix for Docker Hub (explicit "headers": Object { "authorization": "Bearer some-token ", }, - "json": true, + "responseType": "json", }, ], Array [ @@ -103,7 +103,7 @@ exports[`api/docker getPkgReleases adds library/ prefix for Docker Hub (implicit "headers": Object { "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", }, - "json": true, + "responseType": "json", }, ], Array [ @@ -112,7 +112,7 @@ exports[`api/docker getPkgReleases adds library/ prefix for Docker Hub (implicit "headers": Object { "authorization": "Bearer some-token ", }, - "json": true, + "responseType": "json", }, ], Array [ @@ -191,7 +191,7 @@ exports[`api/docker getPkgReleases adds no library/ prefix for other registries "headers": Object { "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", }, - "json": true, + "responseType": "json", }, ], Array [ @@ -200,7 +200,7 @@ exports[`api/docker getPkgReleases adds no library/ prefix for other registries "headers": Object { "authorization": "Bearer some-token ", }, - "json": true, + "responseType": "json", }, ], Array [ @@ -277,7 +277,7 @@ exports[`api/docker getPkgReleases uses custom registry in depName 1`] = ` "https://registry.company.com/v2/node/tags/list?n=10000", Object { "headers": Object {}, - "json": true, + "responseType": "json", }, ], Array [ @@ -325,14 +325,14 @@ Array [ "https://registry.company.com/v2/node/tags/list?n=10000", Object { "headers": Object {}, - "json": true, + "responseType": "json", }, ], Array [ "https://api.github.com/user/9287/repos?page=3&per_page=100", Object { "headers": Object {}, - "json": true, + "responseType": "json", }, ], Array [ @@ -365,7 +365,7 @@ exports[`api/docker getPkgReleases uses lower tag limit for ECR deps 1`] = ` "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/node/tags/list?n=1000", Object { "headers": Object {}, - "json": true, + "responseType": "json", }, ], Array [ diff --git a/lib/datasource/docker/index.ts b/lib/datasource/docker/index.ts index 1000420522fb7f..d435a0ea0ce734 100644 --- a/lib/datasource/docker/index.ts +++ b/lib/datasource/docker/index.ts @@ -3,12 +3,12 @@ import hasha from 'hasha'; import URL from 'url'; import parseLinkHeader from 'parse-link-header'; import wwwAuthenticate from 'www-authenticate'; -import { OutgoingHttpHeaders } from 'http'; import AWS from 'aws-sdk'; +import { Options } from 'got'; import { logger } from '../../logger'; -import got from '../../util/got'; +import got, { GotHeaders, RenovateGotNormalizedOptions } from '../../util/got'; import * as hostRules from '../../util/host-rules'; -import { DatasourceError, GetReleasesConfig, ReleaseResult } from '../common'; +import { DatasourceError, PkgReleaseConfig, ReleaseResult } from '../common'; import { GotResponse } from '../../platform'; // TODO: add got typings when available @@ -58,7 +58,7 @@ export function getRegistryRepository( function getECRAuthToken( region: string, - opts: hostRules.HostRule + opts: Options ): Promise { const config = { region, accessKeyId: undefined, secretAccessKey: undefined }; if (opts.username && opts.password) { @@ -94,7 +94,7 @@ function getECRAuthToken( async function getAuthHeaders( registry: string, repository: string -): Promise { +): Promise { try { const apiCheckUrl = `${registry}/v2/`; const apiCheckResponse = await got(apiCheckUrl, { throwHttpErrors: false }); @@ -105,10 +105,12 @@ async function getAuthHeaders( apiCheckResponse.headers['www-authenticate'] ); - const opts: hostRules.HostRule & { - headers?: Record; - } = hostRules.find({ hostType: id, url: apiCheckUrl }); - opts.json = true; + // TODO: fix types + const opts: any = hostRules.find({ + hostType: id, + url: apiCheckUrl, + }); + opts.responseType = 'json'; if (ecrRegex.test(registry)) { const [, region] = ecrRegex.exec(registry); const auth = await getECRAuthToken(region, opts); @@ -135,7 +137,9 @@ async function getAuthHeaders( logger.trace( `Obtaining docker registry token for ${repository} using url ${authUrl}` ); - const authResponse = (await got(authUrl, opts)).body; + const authResponse = ( + await got<{ token?: string; access_token?: string }>(authUrl, opts) + ).body; const token = authResponse.token || authResponse.access_token; // istanbul ignore if @@ -273,7 +277,7 @@ async function getManifestResponse( * - Return the digest as a string */ export async function getDigest( - { registryUrls, lookupName }: GetReleasesConfig, + { registryUrls, lookupName }: PkgReleaseConfig, newValue?: string ): Promise { const { registry, repository } = getRegistryRepository( @@ -346,7 +350,10 @@ async function getTags( } let page = 1; do { - const res = await got<{ tags: string[] }>(url, { json: true, headers }); + const res = await got<{ tags: string[] }>(url, { + responseType: 'json', + headers, + }); tags = tags.concat(res.body.tags); const linkHeader = parseLinkHeader(res.headers.link as string); url = @@ -413,20 +420,20 @@ async function getTags( export function getConfigResponse( url: string, - headers: OutgoingHttpHeaders + headers: GotHeaders ): Promise { return got(url, { headers, hooks: { beforeRedirect: [ - (options: any): void => { + (options: RenovateGotNormalizedOptions): void => { if ( - options.search && - options.search.indexOf('X-Amz-Algorithm') !== -1 + options.url.search && + options.url.search.indexOf('X-Amz-Algorithm') !== -1 ) { // if there is no port in the redirect URL string, then delete it from the redirect options. // This can be evaluated for removal after upgrading to Got v10 - const portInUrl = options.href.split('/')[2].split(':')[1]; + const portInUrl = options.url.href.split('/')[2].split(':')[1]; if (!portInUrl) { // eslint-disable-next-line no-param-reassign delete options.port; // Redirect will instead use 80 or 443 for HTTP or HTTPS respectively @@ -587,7 +594,7 @@ async function getLabels( export async function getPkgReleases({ lookupName, registryUrls, -}: GetReleasesConfig): Promise { +}: PkgReleaseConfig): Promise { const { registry, repository } = getRegistryRepository( lookupName, registryUrls diff --git a/lib/datasource/galaxy/index.ts b/lib/datasource/galaxy/index.ts index 240f743a74b859..d96d122d0e2a30 100644 --- a/lib/datasource/galaxy/index.ts +++ b/lib/datasource/galaxy/index.ts @@ -37,7 +37,7 @@ export async function getPkgReleases({ try { let res: any = await got(galaxyAPIUrl, { - hostType: id, + context: { hostType: id }, }); if (!res || !res.body) { logger.warn( diff --git a/lib/datasource/go/index.ts b/lib/datasource/go/index.ts index 6eaf0c16bf21a2..dc71ac2fa4a1eb 100644 --- a/lib/datasource/go/index.ts +++ b/lib/datasource/go/index.ts @@ -34,12 +34,12 @@ async function getDatasource(goModule: string): Promise { try { const res = ( await got(pkgUrl, { - hostType: id, + context: { hostType: id }, }) ).body; - const sourceMatch = res.match( - regEx(`(pkgUrl, opts); // istanbul ignore if if (retries < 3) { logger.debug({ pkgUrl, retries }, 'Recovered from npm error'); diff --git a/lib/datasource/npm/index.spec.ts b/lib/datasource/npm/index.spec.ts index 6b146d9ea5d456..5be20c242de29f 100644 --- a/lib/datasource/npm/index.spec.ts +++ b/lib/datasource/npm/index.spec.ts @@ -19,7 +19,7 @@ function getRelease( ); } -describe('api/npm', () => { +describe('datasource/npm', () => { delete process.env.NPM_TOKEN; beforeEach(() => { jest.resetAllMocks(); diff --git a/lib/datasource/nuget/v2.ts b/lib/datasource/nuget/v2.ts index 2d7c36be8d84cb..226e786a680914 100644 --- a/lib/datasource/nuget/v2.ts +++ b/lib/datasource/nuget/v2.ts @@ -21,7 +21,7 @@ export async function getPkgReleases( let pkgUrlList = `${feedUrl}/FindPackagesById()?id=%27${pkgName}%27&$select=Version,IsLatestVersion,ProjectUrl`; do { const pkgVersionsListRaw = await got(pkgUrlList, { - hostType: id, + context: { hostType: id }, }); if (pkgVersionsListRaw.statusCode !== 200) { logger.debug( diff --git a/lib/datasource/nuget/v3.ts b/lib/datasource/nuget/v3.ts index 9bbb381014e829..de7b2504b8aca8 100644 --- a/lib/datasource/nuget/v3.ts +++ b/lib/datasource/nuget/v3.ts @@ -1,7 +1,7 @@ import * as semver from 'semver'; import { XmlDocument } from 'xmldoc'; import { logger } from '../../logger'; -import got from '../../util/got'; +import got, { GotResponse } from '../../util/got'; import { ReleaseResult } from '../common'; import { id } from './common'; @@ -29,9 +29,10 @@ export async function getQueryUrl(url: string): Promise { } try { - const servicesIndexRaw = await got(url, { - json: true, - hostType: id, + // TODO: fix types + const servicesIndexRaw = await got<{ resources: any[] }>(url, { + responseType: 'json', + context: { hostType: id }, }); if (servicesIndexRaw.statusCode !== 200) { logger.debug( @@ -78,9 +79,10 @@ export async function getPkgReleases( releases: [], }; try { - const pkgUrlListRaw = await got(queryUrl, { - json: true, - hostType: id, + // TODO: fix types + const pkgUrlListRaw = await got<{ data: any[] }>(queryUrl, { + responseType: 'json', + context: { hostType: id }, }); if (pkgUrlListRaw.statusCode !== 200) { logger.debug( @@ -119,9 +121,11 @@ export async function getPkgReleases( } if (registryUrl.toLowerCase() === defaultNugetFeed.toLowerCase()) { const nugetOrgApi = `https://api.nuget.org/v3-flatcontainer/${pkgName.toLowerCase()}/${lastVersion}/${pkgName.toLowerCase()}.nuspec`; - let metaresult: { body: string }; + let metaresult: GotResponse; try { - metaresult = await got(nugetOrgApi, { hostType: id }); + metaresult = await got(nugetOrgApi, { + context: { hostType: id }, + }); } catch (err) /* istanbul ignore next */ { logger.debug( `Cannot fetch metadata for ${pkgName} using popped version ${lastVersion}` diff --git a/lib/datasource/orb/index.ts b/lib/datasource/orb/index.ts index 24385098637109..ca50897f3f265f 100644 --- a/lib/datasource/orb/index.ts +++ b/lib/datasource/orb/index.ts @@ -36,11 +36,11 @@ export async function getPkgReleases({ variables: {}, }; try { - const res: OrbRelease = ( - await got.post(url, { - body, - hostType: id, - json: true, + const res = ( + await got.post<{ data: { orb: OrbRelease } }>(url, { + json: body, + responseType: 'json', + context: { hostType: id }, retry: 5, }) ).body.data.orb; diff --git a/lib/datasource/packagist/index.ts b/lib/datasource/packagist/index.ts index 8c989a7ca38b5b..8a99de8981f754 100644 --- a/lib/datasource/packagist/index.ts +++ b/lib/datasource/packagist/index.ts @@ -13,14 +13,15 @@ export const id = 'packagist'; function getHostOpts(url: string): GotJSONOptions { const opts: GotJSONOptions = { - json: true, + responseType: 'json', }; const { username, password } = hostRules.find({ hostType: id, url, }); if (username && password) { - opts.auth = `${username}:${password}`; + opts.username = username; + opts.password = password; } return opts; } @@ -47,7 +48,7 @@ async function getRegistryMeta(regUrl: string): Promise { try { const url = URL.resolve(regUrl.replace(/\/?$/, '/'), 'packages.json'); const opts = getHostOpts(url); - const res: PackageMeta = (await got(url, opts)).body; + const res = (await got(url, opts)).body; const meta: RegistryMeta = {}; meta.packages = res.packages; if (res.includes) { @@ -106,8 +107,8 @@ async function getPackagistFile( const { key, sha256 } = file; const fileName = key.replace('%hash%', sha256); const opts = getHostOpts(regUrl); - if (opts.auth || (opts.headers && opts.headers.authorization)) { - return (await got(regUrl + '/' + fileName, opts)).body; + if (opts.username || opts.headers?.authorization) { + return (await got(regUrl + '/' + fileName, opts)).body; } const cacheNamespace = 'datasource-packagist-files'; const cacheKey = regUrl + key; @@ -117,7 +118,7 @@ async function getPackagistFile( if (cachedResult && cachedResult.sha256 === sha256) { return cachedResult.res; } - const res = (await got(regUrl + '/' + fileName, opts)).body; + const res = (await got(regUrl + '/' + fileName, opts)).body; const cacheMinutes = 1440; // 1 day await renovateCache.set( cacheNamespace, @@ -223,9 +224,10 @@ async function packagistOrgLookup(name: string): Promise { let dep: ReleaseResult = null; const regUrl = 'https://packagist.org'; const pkgUrl = URL.resolve(regUrl, `/p/${name}.json`); + // TODO: fix types const res = ( - await got(pkgUrl, { - json: true, + await got(pkgUrl, { + responseType: 'json', retry: 5, }) ).body.packages[name]; @@ -276,7 +278,9 @@ async function packageLookup( .replace('%hash%', providerPackages[name]) ); const opts = getHostOpts(regUrl); - const versions = (await got(pkgUrl, opts)).body.packages[name]; + const versions = ( + await got<{ packages: Record }>(pkgUrl, opts) + ).body.packages[name]; const dep = extractDepReleases(versions); dep.name = name; logger.trace({ dep }, 'dep'); diff --git a/lib/datasource/pypi/__snapshots__/index.spec.ts.snap b/lib/datasource/pypi/__snapshots__/index.spec.ts.snap index b1e8f65facc004..8caeabcd4ce05a 100644 --- a/lib/datasource/pypi/__snapshots__/index.spec.ts.snap +++ b/lib/datasource/pypi/__snapshots__/index.spec.ts.snap @@ -215,23 +215,12 @@ Object { exports[`datasource/pypi getPkgReleases supports custom datasource url 1`] = ` Array [ Array [ - Url { - "auth": null, - "hash": null, - "host": "custom.pypi.net", - "hostname": "custom.pypi.net", - "href": "https://custom.pypi.net/foo/azure-cli-monitor/json", - "path": "/foo/azure-cli-monitor/json", - "pathname": "/foo/azure-cli-monitor/json", - "port": null, - "protocol": "https:", - "query": null, - "search": null, - "slashes": true, - }, - Object { - "hostType": "pypi", - "json": true, + "https://custom.pypi.net/foo/azure-cli-monitor/json", + Object { + "context": Object { + "hostType": "pypi", + }, + "responseType": "json", }, ], ] @@ -240,23 +229,12 @@ Array [ exports[`datasource/pypi getPkgReleases supports custom datasource url from environmental variable 1`] = ` Array [ Array [ - Url { - "auth": null, - "hash": null, - "host": "my.pypi.python", - "hostname": "my.pypi.python", - "href": "https://my.pypi.python/pypi/azure-cli-monitor/json", - "path": "/pypi/azure-cli-monitor/json", - "pathname": "/pypi/azure-cli-monitor/json", - "port": null, - "protocol": "https:", - "query": null, - "search": null, - "slashes": true, - }, - Object { - "hostType": "pypi", - "json": true, + "https://my.pypi.python/pypi/azure-cli-monitor/json", + Object { + "context": Object { + "hostType": "pypi", + }, + "responseType": "json", }, ], ] @@ -265,43 +243,21 @@ Array [ exports[`datasource/pypi getPkgReleases supports multiple custom datasource urls 1`] = ` Array [ Array [ - Url { - "auth": null, - "hash": null, - "host": "custom.pypi.net", - "hostname": "custom.pypi.net", - "href": "https://custom.pypi.net/foo/azure-cli-monitor/json", - "path": "/foo/azure-cli-monitor/json", - "pathname": "/foo/azure-cli-monitor/json", - "port": null, - "protocol": "https:", - "query": null, - "search": null, - "slashes": true, - }, - Object { - "hostType": "pypi", - "json": true, + "https://custom.pypi.net/foo/azure-cli-monitor/json", + Object { + "context": Object { + "hostType": "pypi", + }, + "responseType": "json", }, ], Array [ - Url { - "auth": null, - "hash": null, - "host": "second-index", - "hostname": "second-index", - "href": "https://second-index/foo/azure-cli-monitor/json", - "path": "/foo/azure-cli-monitor/json", - "pathname": "/foo/azure-cli-monitor/json", - "port": null, - "protocol": "https:", - "query": null, - "search": null, - "slashes": true, - }, - Object { - "hostType": "pypi", - "json": true, + "https://second-index/foo/azure-cli-monitor/json", + Object { + "context": Object { + "hostType": "pypi", + }, + "responseType": "json", }, ], ] diff --git a/lib/datasource/pypi/index.ts b/lib/datasource/pypi/index.ts index bab4dc7a980bff..d75b3b2fef0115 100644 --- a/lib/datasource/pypi/index.ts +++ b/lib/datasource/pypi/index.ts @@ -38,9 +38,10 @@ async function getDependency( const lookupUrl = url.resolve(hostUrl, `${packageName}/json`); try { const dependency: ReleaseResult = { releases: null }; - const rep = await got(url.parse(lookupUrl), { - json: true, - hostType: id, + // TODO: fix type + const rep = await got(lookupUrl, { + responseType: 'json', + context: { hostType: id }, }); const dep = rep && rep.body; if (!dep) { @@ -121,8 +122,9 @@ async function getSimpleDependency( const lookupUrl = url.resolve(hostUrl, `${packageName}`); try { const dependency: ReleaseResult = { releases: null }; - const response = await got(url.parse(lookupUrl), { - hostType: id, + // TODO: fix me + const response = await got(url.parse(lookupUrl)?.toString(), { + context: { hostType: id }, }); const dep = response && response.body; if (!dep) { diff --git a/lib/datasource/rubygems/get.ts b/lib/datasource/rubygems/get.ts index 9a32f01c654b86..931cbdee418eaf 100644 --- a/lib/datasource/rubygems/get.ts +++ b/lib/datasource/rubygems/get.ts @@ -1,8 +1,6 @@ -import { OutgoingHttpHeaders } from 'http'; import { logger } from '../../logger'; import got from '../../util/got'; import { maskToken } from '../../util/mask'; -import retriable from './retriable'; import { UNAUTHORIZED, FORBIDDEN, NOT_FOUND } from './errors'; import { ReleaseResult } from '../common'; import { id } from './common'; @@ -30,21 +28,16 @@ const processError = ({ err, ...rest }): null => { return null; }; -const getHeaders = (): OutgoingHttpHeaders => { - return { hostType: id }; -}; - const fetch = async ({ dependency, registry, path }): Promise => { - const json = true; - - const retry = { retries: retriable() }; - const headers = getHeaders(); - const name = `${path}/${dependency}.json`; const baseUrl = registry; logger.trace({ dependency }, `RubyGems lookup request: ${baseUrl} ${name}`); - const response = (await got(name, { retry, json, baseUrl, headers })) || { + const response = (await got(name, { + responseType: 'json', + prefixUrl: baseUrl, + context: { hostType: id }, + })) || { body: undefined, }; diff --git a/lib/datasource/rubygems/retriable.spec.ts b/lib/datasource/rubygems/retriable.spec.ts deleted file mode 100644 index 19ddba9fe7c76a..00000000000000 --- a/lib/datasource/rubygems/retriable.spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -import retriable from './retriable'; - -describe('datasource/rubygems/retriable', () => { - it('returns 0 when numberOfRetries equals 0', () => { - expect(retriable(0)(null, null)).toEqual(0); - }); - - it('returns retry after header + 1 second if request is banned', () => { - expect( - retriable(1)(null, { - statusCode: 429, - headers: { 'retry-after': '5' }, - }) - ).toEqual(6000); - - expect( - retriable(1)(null, { - statusCode: 503, - headers: { 'retry-after': '9' }, - }) - ).toEqual(10000); - }); - - it('returns default delay if request is not banned', () => { - expect(retriable(1)(null, { statusCode: 500 })).toEqual(2000); - }); - - it('uses default numberOfRetries', () => { - expect(retriable()(null, { statusCode: 500 })).toEqual(1000); - }); -}); diff --git a/lib/datasource/rubygems/retriable.ts b/lib/datasource/rubygems/retriable.ts deleted file mode 100644 index 8d85ed2c4ef6be..00000000000000 --- a/lib/datasource/rubygems/retriable.ts +++ /dev/null @@ -1,63 +0,0 @@ -import got from 'got'; -import { logger } from '../../logger'; -import { - UNAUTHORIZED, - FORBIDDEN, - REQUEST_TIMEOUT, - TOO_MANY_REQUEST, - SERVICE_UNAVAILABLE, -} from './errors'; - -const DEFAULT_BANNED_RETRY_AFTER = 600; -const NUMBER_OF_RETRIES = 2; - -const getBannedDelay = (retryAfter: string): number => - (parseInt(retryAfter, 10) || DEFAULT_BANNED_RETRY_AFTER) + 1; -const getDefaultDelay = (count: number): number => - +(NUMBER_OF_RETRIES / count).toFixed(3); - -const getErrorMessage = (status: number): string => { - // istanbul ignore next - switch (status) { - case UNAUTHORIZED: - case FORBIDDEN: - return `RubyGems registry: Authentication failed.`; - case TOO_MANY_REQUEST: - return `RubyGems registry: Too Many Requests.`; - case REQUEST_TIMEOUT: - case SERVICE_UNAVAILABLE: - return `RubyGems registry: Temporary Unavailable`; - default: - return `RubyGems registry: Internal Server Error`; - } -}; - -// TODO: workaround because got does not export HTTPError, should be moved to `lib/util/got` -export type HTTPError = InstanceType; - -export default (numberOfRetries = NUMBER_OF_RETRIES): got.RetryFunction => ( - _?: number, - err?: Partial -): number => { - if (numberOfRetries === 0) { - return 0; - } - - const { headers, statusCode } = err; - - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After - const isBanned = [TOO_MANY_REQUEST, SERVICE_UNAVAILABLE].includes(statusCode); - const delaySec = isBanned - ? getBannedDelay(headers['retry-after']) - : getDefaultDelay(numberOfRetries); - - // eslint-disable-next-line - numberOfRetries--; - - const errorMessage = getErrorMessage(statusCode); - const message = `${errorMessage} Retry in ${delaySec} seconds.`; - - logger.debug(message); - - return delaySec * 1000; -}; diff --git a/lib/datasource/sbt-plugin/index.spec.ts b/lib/datasource/sbt-plugin/index.spec.ts index d7c34792dfedbe..762df59eb4ef5f 100644 --- a/lib/datasource/sbt-plugin/index.spec.ts +++ b/lib/datasource/sbt-plugin/index.spec.ts @@ -25,6 +25,7 @@ describe('datasource/sbt', () => { describe('getPkgReleases', () => { beforeEach(() => { + nock.cleanAll(); nock.disableNetConnect(); nock('https://failed_repo') .get('/maven/org/scalatest/') diff --git a/lib/datasource/terraform-module/index.ts b/lib/datasource/terraform-module/index.ts index a9578d7753263f..c16b505b0c7775 100644 --- a/lib/datasource/terraform-module/index.ts +++ b/lib/datasource/terraform-module/index.ts @@ -72,10 +72,10 @@ export async function getPkgReleases({ return cachedResult; } try { - const res: TerraformRelease = ( - await got(pkgUrl, { - json: true, - hostType: id, + const res = ( + await got(pkgUrl, { + responseType: 'json', + context: { hostType: id }, }) ).body; const returnedName = res.namespace + '/' + res.name + '/' + res.provider; diff --git a/lib/datasource/terraform-provider/index.ts b/lib/datasource/terraform-provider/index.ts index 0c3544d3bd65e9..0ff2be26cc4b2b 100644 --- a/lib/datasource/terraform-provider/index.ts +++ b/lib/datasource/terraform-provider/index.ts @@ -35,10 +35,10 @@ export async function getPkgReleases({ return cachedResult; } try { - const res: TerraformProvider = ( - await got(pkgUrl, { - json: true, - hostType: id, + const res = ( + await got(pkgUrl, { + responseType: 'json', + context: { hostType: id }, }) ).body; // Simplify response before caching and returning diff --git a/lib/logger/err-serializer.ts b/lib/logger/err-serializer.ts index 509870003615db..5bf624eb53f885 100644 --- a/lib/logger/err-serializer.ts +++ b/lib/logger/err-serializer.ts @@ -18,22 +18,16 @@ export default function errSerializer(err: any): any { if (err.stack) { response.stack = err.stack; } - if (response.gotOptions) { - if (is.string(response.gotOptions.auth)) { - response.gotOptions.auth = response.gotOptions.auth.replace( - /:.*/, - ':***********' - ); - } - if (err.gotOptions.headers) { + if (response.options) { + if (err.options.headers) { const redactedHeaders = [ 'authorization', 'private-header', 'Private-header', ]; redactedHeaders.forEach(header => { - if (response.gotOptions.headers[header]) { - response.gotOptions.headers[header] = '** redacted **'; + if (response.options.headers[header]) { + response.options.headers[header] = '** redacted **'; } }); } diff --git a/lib/manager/gradle-wrapper/update.ts b/lib/manager/gradle-wrapper/update.ts index 9d688c98e0cfe1..5a8be8252ace8c 100644 --- a/lib/manager/gradle-wrapper/update.ts +++ b/lib/manager/gradle-wrapper/update.ts @@ -9,7 +9,7 @@ function replaceType(url: string): string { async function getChecksum(url: string): Promise { try { const response = await got(url); - return response.body as string; + return response.body; } catch (err) { if (err.statusCode === 404 || err.code === 'ENOTFOUND') { logger.debug('Gradle checksum lookup failure: not found'); diff --git a/lib/manager/npm/extract/common.ts b/lib/manager/npm/extract/common.ts index e7300c1d403f2f..cd8bca151f3523 100644 --- a/lib/manager/npm/extract/common.ts +++ b/lib/manager/npm/extract/common.ts @@ -1,4 +1,3 @@ -// eslint-disable-next-line import/no-unresolved import { PackageJson } from 'type-fest'; export interface NpmPackage extends PackageJson { diff --git a/lib/manager/npm/post-update/index.ts b/lib/manager/npm/post-update/index.ts index 26e5bcce4a1bbd..7088628098ac15 100644 --- a/lib/manager/npm/post-update/index.ts +++ b/lib/manager/npm/post-update/index.ts @@ -1,7 +1,6 @@ import fs from 'fs-extra'; import path from 'path'; import upath from 'upath'; -// eslint-disable-next-line import/no-unresolved import { PackageJson } from 'type-fest'; import { logger } from '../../../logger'; import * as npm from './npm'; diff --git a/lib/platform/bitbucket-server/bb-got-wrapper.ts b/lib/platform/bitbucket-server/bb-got-wrapper.ts index d020bb04ea0726..8f7212b5ef0e88 100644 --- a/lib/platform/bitbucket-server/bb-got-wrapper.ts +++ b/lib/platform/bitbucket-server/bb-got-wrapper.ts @@ -1,35 +1,34 @@ -import URL from 'url'; -import { GotJSONOptions } from 'got'; -import got from '../../util/got'; +import got, { GotJSONOptions, GotMethod } from '../../util/got'; import { GotApi, GotApiOptions, GotResponse } from '../common'; import { PLATFORM_TYPE_BITBUCKET_SERVER } from '../../constants/platforms'; +const hostType = PLATFORM_TYPE_BITBUCKET_SERVER; let baseUrl: string; -function get( - path: string, - options: GotApiOptions & GotJSONOptions -): Promise { - const url = URL.resolve(baseUrl, path); - const opts: GotApiOptions & GotJSONOptions = { - hostType: PLATFORM_TYPE_BITBUCKET_SERVER, - json: true, - ...options, +function get(path: string, options: GotApiOptions): Promise { + const opts: GotJSONOptions = { + prefixUrl: baseUrl, + json: options.body, + headers: options.headers, + ...options.options, + responseType: 'json', + method: options.method, + context: { hostType, ...options.options?.context }, }; opts.headers = { ...opts.headers, 'X-Atlassian-Token': 'no-check', }; - return got(url, opts); + return got(path, opts); } -const helpers = ['get', 'post', 'put', 'patch', 'head', 'delete']; +const helpers: GotMethod[] = ['get', 'post', 'put', 'patch', 'head', 'delete']; export const api: GotApi = {} as any; -for (const x of helpers) { - (api as any)[x] = (url: string, opts: any): Promise => - get(url, { ...opts, method: x.toUpperCase() }); +for (const method of helpers) { + (api as any)[method] = (url: string, opts: any): Promise => + get(url, { ...opts, method }); } // eslint-disable-next-line @typescript-eslint/unbound-method diff --git a/lib/platform/bitbucket-server/utils.ts b/lib/platform/bitbucket-server/utils.ts index 0de65c5bd28b2d..6383baf4af90b0 100644 --- a/lib/platform/bitbucket-server/utils.ts +++ b/lib/platform/bitbucket-server/utils.ts @@ -1,7 +1,8 @@ // SEE for the reference https://github.com/renovatebot/renovate/blob/c3e9e572b225085448d94aa121c7ec81c14d3955/lib/platform/bitbucket/utils.js import url from 'url'; import { api } from './bb-got-wrapper'; -import { Pr } from '../common'; +import { Pr, GotApiOptions } from '../common'; +import { GotMethod } from '../../util/got'; // https://docs.atlassian.com/bitbucket-server/rest/6.0.0/bitbucket-rest.html#idp250 const prStateMapping: any = { @@ -35,8 +36,8 @@ const addMaxLength = (inputUrl: string, limit = 100): string => { export async function accumulateValues( reqUrl: string, - method = 'get', - options?: any, + method: GotMethod = 'get', + options?: GotApiOptions, limit?: number ): Promise { let accumulator: T[] = []; diff --git a/lib/platform/bitbucket/bb-got-wrapper.ts b/lib/platform/bitbucket/bb-got-wrapper.ts index e1072c3c68ac91..0340205297ad32 100644 --- a/lib/platform/bitbucket/bb-got-wrapper.ts +++ b/lib/platform/bitbucket/bb-got-wrapper.ts @@ -1,29 +1,43 @@ -import { GotJSONOptions } from 'got'; -import got from '../../util/got'; +import got, { GotJSONOptions, GotMethod } from '../../util/got'; import { GotApi, GotApiOptions, GotResponse } from '../common'; import { PLATFORM_TYPE_BITBUCKET } from '../../constants/platforms'; +const hostType = PLATFORM_TYPE_BITBUCKET; + async function get( path: string, - options: GotApiOptions & GotJSONOptions + options: BitBucketApiOptions ): Promise { - const opts: GotApiOptions & GotJSONOptions = { - json: true, - hostType: PLATFORM_TYPE_BITBUCKET, - baseUrl: 'https://api.bitbucket.org/', - ...options, + const opts: GotJSONOptions = { + prefixUrl: 'https://api.bitbucket.org/', + json: options.body, + headers: options.headers, + ...options.options, + responseType: 'json', + method: options.method, + context: { hostType, ...options.options?.context }, }; + + // response expected as string + if (options.json === false) (opts as any).responseType = 'text'; const res = await got(path, opts); return res; } -const helpers = ['get', 'post', 'put', 'patch', 'head', 'delete']; +const helpers: GotMethod[] = ['get', 'post', 'put', 'patch', 'head', 'delete']; + +export interface BitBucketApiOptions extends GotApiOptions { + /** + * response should be string instead of json + */ + json?: false; +} -export const api: GotApi = {} as any; +export const api: GotApi = {} as any; -for (const x of helpers) { - (api as any)[x] = (url: string, opts: any): Promise => - get(url, { ...opts, method: x.toUpperCase() }); +for (const method of helpers) { + (api as any)[method] = (url: string, opts: any): Promise => + get(url, { ...opts, method }); } export default api; diff --git a/lib/platform/bitbucket/index.ts b/lib/platform/bitbucket/index.ts index 5c872d33c90558..8993fa08303143 100644 --- a/lib/platform/bitbucket/index.ts +++ b/lib/platform/bitbucket/index.ts @@ -301,7 +301,7 @@ async function isPrConflicted(prNo: number): Promise { const diff = ( await api.get( `/2.0/repositories/${config.repository}/pullrequests/${prNo}/diff`, - { json: false } as any + { json: false } ) ).body; @@ -781,7 +781,7 @@ export async function getPrFiles(prNo: number): Promise { const diff = ( await api.get( `/2.0/repositories/${config.repository}/pullrequests/${prNo}/diff`, - { json: false } as any + { json: false } ) ).body; const files = parseDiff(diff).map(file => file.to); diff --git a/lib/platform/bitbucket/utils.ts b/lib/platform/bitbucket/utils.ts index 51d004006875f0..e2304afb57ad12 100644 --- a/lib/platform/bitbucket/utils.ts +++ b/lib/platform/bitbucket/utils.ts @@ -1,7 +1,7 @@ import url from 'url'; import { api } from './bb-got-wrapper'; import { Storage } from '../git/storage'; -import { GotResponse, Pr } from '../common'; +import { GotResponse, Pr, GotApiOptions } from '../common'; export interface Config { baseBranch: string; @@ -81,7 +81,7 @@ const addMaxLength = (inputUrl: string, pagelen = 100): string => { export async function accumulateValues( reqUrl: string, method = 'get', - options?: any, + options?: GotApiOptions, pagelen?: number ): Promise { let accumulator: T[] = []; diff --git a/lib/platform/common.ts b/lib/platform/common.ts index 224bcbd2f25d1e..a7b8b0fc61b738 100644 --- a/lib/platform/common.ts +++ b/lib/platform/common.ts @@ -1,5 +1,6 @@ -import got from 'got'; import Git from 'simple-git/promise'; +import { Method } from 'got'; +import { GotResponse as Response, GotOptions, GotHeaders } from '../util/got'; import { RenovateConfig } from '../config/common'; import { CommitFilesConfig } from './git/storage'; @@ -10,14 +11,22 @@ export interface FileData { export interface GotApiOptions { useCache?: boolean; - hostType?: string; - body?: any; + body?: unknown; + + headers?: GotHeaders; + method?: Method; + options?: GotOptions; + pageLimit?: number; + paginate?: boolean | 'all'; } -export type GotResponse = got.Response; +/** + * @deprecated use GotResponse from `util/got` + */ +export type GotResponse = Response; -export interface GotApi { - get( +export interface GotApi { + /** @deprecated use other */ get( url: string, options?: GotApiOptions & TOptions ): Promise>; diff --git a/lib/platform/gitea/gitea-got-wrapper.ts b/lib/platform/gitea/gitea-got-wrapper.ts index 84bac293aa631b..d62ef3bd5930ee 100644 --- a/lib/platform/gitea/gitea-got-wrapper.ts +++ b/lib/platform/gitea/gitea-got-wrapper.ts @@ -1,7 +1,13 @@ import URL from 'url'; import { GotApi, GotApiOptions, GotResponse } from '../common'; import { PLATFORM_TYPE_GITEA } from '../../constants/platforms'; -import got from '../../util/got'; +import got, { GotJSONOptions, GotMethod } from '../../util/got'; + +declare module 'http' { + interface IncomingHttpHeaders { + 'x-total-count'?: string; + } +} const hostType = PLATFORM_TYPE_GITEA; let baseUrl: string; @@ -17,19 +23,24 @@ function getPaginationContainer(body: any): any[] { return null; } -async function get(path: string, options?: any): Promise { - const opts = { - hostType, - baseUrl, - json: true, - ...options, +async function get( + path: string, + options?: GiteaApiOptions +): Promise { + const opts: GotJSONOptions = { + prefixUrl: baseUrl, + json: options.body, + headers: options.headers, + ...options.options, + responseType: 'json', + context: { hostType, ...options.options?.context }, }; const res = await got(path, opts); const pc = getPaginationContainer(res.body); - if (opts.paginate && pc) { + if (options.paginate && pc) { const url = URL.parse(res.url, true); - const total = parseInt(res.headers['x-total-count'] as string, 10); + const total = parseInt(res.headers['x-total-count'], 10); let nextPage = parseInt(url.query.page as string, 10) || 1 + 1; while (total && pc.length < total) { @@ -44,22 +55,19 @@ async function get(path: string, options?: any): Promise { return res; } -const helpers = ['get', 'post', 'put', 'patch', 'head', 'delete']; +const helpers: GotMethod[] = ['get', 'post', 'put', 'patch', 'head', 'delete']; -export type GiteaGotOptions = { - paginate?: boolean; - token?: string; -} & GotApiOptions; +export type GiteaApiOptions = GotApiOptions; -export interface GiteaGotApi extends GotApi { +export interface GiteaGotApi extends GotApi { setBaseUrl(url: string): void; } export const api: GiteaGotApi = {} as any; -for (const x of helpers) { - (api as any)[x] = (path: string, options: any): Promise => - get(path, { ...options, method: x.toUpperCase() }); +for (const method of helpers) { + (api as any)[method] = (path: string, options: any): Promise => + get(path, { ...options, method }); } // eslint-disable-next-line @typescript-eslint/unbound-method diff --git a/lib/platform/gitea/gitea-helper.ts b/lib/platform/gitea/gitea-helper.ts index c96b559a930181..6e94001fd2f472 100644 --- a/lib/platform/gitea/gitea-helper.ts +++ b/lib/platform/gitea/gitea-helper.ts @@ -1,5 +1,5 @@ import { URLSearchParams } from 'url'; -import { api, GiteaGotOptions } from './gitea-got-wrapper'; +import { api, GiteaApiOptions } from './gitea-got-wrapper'; import { GotResponse } from '../common'; export type PRState = 'open' | 'closed' | 'all'; @@ -197,20 +197,20 @@ function queryParams(params: Record): URLSearchParams { return usp; } -export async function getCurrentUser(options?: GiteaGotOptions): Promise { +export async function getCurrentUser(options?: GiteaApiOptions): Promise { const url = 'user'; - const res: GotResponse = await api.get(url, options); + const res = await api.get(url, options); return res.body; } export async function searchRepos( params: RepoSearchParams, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { const query = queryParams(params).toString(); const url = `repos/search?${query}`; - const res: GotResponse = await api.get(url, { + const res = await api.get(url, { ...options, paginate: true, }); @@ -226,10 +226,10 @@ export async function searchRepos( export async function getRepo( repoPath: string, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { const url = `repos/${repoPath}`; - const res: GotResponse = await api.get(url, options); + const res = await api.get(url, options); return res.body; } @@ -238,11 +238,11 @@ export async function getRepoContents( repoPath: string, filePath: string, ref?: string, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { const query = queryParams(ref ? { ref } : {}).toString(); const url = `repos/${repoPath}/contents/${urlEscape(filePath)}?${query}`; - const res: GotResponse = await api.get(url, options); + const res = await api.get(url, options); if (res.body.content) { res.body.contentString = Buffer.from(res.body.content, 'base64').toString(); @@ -254,7 +254,7 @@ export async function getRepoContents( export async function createPR( repoPath: string, params: PRCreateParams, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { const url = `repos/${repoPath}/pulls`; const res: GotResponse = await api.post(url, { @@ -269,7 +269,7 @@ export async function updatePR( repoPath: string, idx: number, params: PRUpdateParams, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { const url = `repos/${repoPath}/pulls/${idx}`; const res: GotResponse = await api.patch(url, { @@ -283,19 +283,23 @@ export async function updatePR( export async function closePR( repoPath: string, idx: number, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { - await updatePR(repoPath, idx, { - ...options, - state: 'closed', - }); + await updatePR( + repoPath, + idx, + { + state: 'closed', + }, + options + ); } export async function mergePR( repoPath: string, idx: number, method: PRMergeMethod, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { const params: PRMergeParams = { Do: method }; const url = `repos/${repoPath}/pulls/${idx}/merge`; @@ -308,10 +312,10 @@ export async function mergePR( export async function getPR( repoPath: string, idx: number, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { const url = `repos/${repoPath}/pulls/${idx}`; - const res: GotResponse = await api.get(url, options); + const res = await api.get(url, options); return res.body; } @@ -319,11 +323,11 @@ export async function getPR( export async function searchPRs( repoPath: string, params: PRSearchParams, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { const query = queryParams(params).toString(); const url = `repos/${repoPath}/pulls?${query}`; - const res: GotResponse = await api.get(url, { + const res = await api.get(url, { ...options, paginate: true, }); @@ -334,10 +338,10 @@ export async function searchPRs( export async function createIssue( repoPath: string, params: IssueCreateParams, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { const url = `repos/${repoPath}/issues`; - const res: GotResponse = await api.post(url, { + const res = await api.post(url, { ...options, body: params, }); @@ -349,10 +353,10 @@ export async function updateIssue( repoPath: string, idx: number, params: IssueUpdateParams, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { const url = `repos/${repoPath}/issues/${idx}`; - const res: GotResponse = await api.patch(url, { + const res = await api.patch(url, { ...options, body: params, }); @@ -363,22 +367,26 @@ export async function updateIssue( export async function closeIssue( repoPath: string, idx: number, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { - await updateIssue(repoPath, idx, { - ...options, - state: 'closed', - }); + await updateIssue( + repoPath, + idx, + { + state: 'closed', + }, + options + ); } export async function searchIssues( repoPath: string, params: IssueSearchParams, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { const query = queryParams(params).toString(); const url = `repos/${repoPath}/issues?${query}`; - const res: GotResponse = await api.get(url, { + const res = await api.get(url, { ...options, paginate: true, }); @@ -388,10 +396,10 @@ export async function searchIssues( export async function getRepoLabels( repoPath: string, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { const url = `repos/${repoPath}/labels`; - const res: GotResponse = await api.get(url, options); + const res = await api.get(url, options); return res.body; } @@ -400,7 +408,7 @@ export async function unassignLabel( repoPath: string, issue: number, label: number, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { const url = `repos/${repoPath}/issues/${issue}/labels/${label}`; await api.delete(url, options); @@ -410,11 +418,11 @@ export async function createComment( repoPath: string, issue: number, body: string, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { const params: CommentCreateParams = { body }; const url = `repos/${repoPath}/issues/${issue}/comments`; - const res: GotResponse = await api.post(url, { + const res: GotResponse = await api.post(url, { ...options, body: params, }); @@ -426,11 +434,11 @@ export async function updateComment( repoPath: string, idx: number, body: string, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { const params: CommentUpdateParams = { body }; const url = `repos/${repoPath}/issues/comments/${idx}`; - const res: GotResponse = await api.patch(url, { + const res = await api.patch(url, { ...options, body: params, }); @@ -441,7 +449,7 @@ export async function updateComment( export async function deleteComment( repoPath, idx: number, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { const url = `repos/${repoPath}/issues/comments/${idx}`; await api.delete(url, options); @@ -450,10 +458,10 @@ export async function deleteComment( export async function getComments( repoPath, issue: number, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { const url = `repos/${repoPath}/issues/${issue}/comments`; - const res: GotResponse = await api.get(url, options); + const res = await api.get(url, options); return res.body; } @@ -462,10 +470,10 @@ export async function createCommitStatus( repoPath: string, branchCommit: string, params: CommitStatusCreateParams, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { const url = `repos/${repoPath}/statuses/${branchCommit}`; - const res: GotResponse = await api.post(url, { + const res = await api.post(url, { ...options, body: params, }); @@ -476,10 +484,10 @@ export async function createCommitStatus( export async function getCombinedCommitStatus( repoPath: string, branchName: string, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { const url = `repos/${repoPath}/commits/${urlEscape(branchName)}/statuses`; - const res: GotResponse = await api.get(url, { + const res = await api.get(url, { ...options, paginate: true, }); @@ -498,10 +506,10 @@ export async function getCombinedCommitStatus( export async function getBranch( repoPath: string, branchName: string, - options?: GiteaGotOptions + options?: GiteaApiOptions ): Promise { const url = `repos/${repoPath}/branches/${urlEscape(branchName)}`; - const res: GotResponse = await api.get(url, options); + const res = await api.get(url, options); return res.body; } diff --git a/lib/platform/gitea/index.ts b/lib/platform/gitea/index.ts index b6ac40abeb90a2..f10bcf299ce2d0 100644 --- a/lib/platform/gitea/index.ts +++ b/lib/platform/gitea/index.ts @@ -205,8 +205,11 @@ const platform: Platform = { api.setBaseUrl(defaults.endpoint); let gitAuthor: string; + const options = { context: { token } }; try { - const user = await helper.getCurrentUser({ token }); + const user = await helper.getCurrentUser({ + options, + }); gitAuthor = `${user.full_name || user.username} <${user.email}>`; botUserID = user.id; } catch (err) { diff --git a/lib/platform/github/gh-got-wrapper.ts b/lib/platform/github/gh-got-wrapper.ts index 132cf50baa1848..be8068f6193e68 100644 --- a/lib/platform/github/gh-got-wrapper.ts +++ b/lib/platform/github/gh-got-wrapper.ts @@ -3,9 +3,9 @@ import parseLinkHeader from 'parse-link-header'; import pAll from 'p-all'; import { GotError } from 'got'; -import got, { GotJSONOptions } from '../../util/got'; +import got, { GotJSONOptions, GotMethod } from '../../util/got'; import { maskToken } from '../../util/mask'; -import { GotApi, GotResponse } from '../common'; +import { GotApi, GotResponse, GotApiOptions } from '../common'; import { logger } from '../../logger'; import { PLATFORM_BAD_CREDENTIALS, @@ -30,9 +30,7 @@ type GotRequestError = GotError & { headers?: Record; }; -type GotRequestOptions = GotJSONOptions & { - token?: string; -}; +type GotRequestOptions = GotJSONOptions; export function dispatchError( err: GotRequestError, @@ -89,7 +87,7 @@ export function dispatchError( const rateLimit = err.headers ? err.headers['x-ratelimit-limit'] : -1; logger.debug( { - token: maskToken(opts.token), + token: maskToken(opts.context?.token), err, }, 'GitHub failure: Bad credentials' @@ -119,21 +117,29 @@ export function dispatchError( async function get( path: string, - options?: any, + options?: GithubApiOptions, okToRetry = true ): Promise { - let result = null; + let result: GotResponse = null; - const opts = { - hostType, - baseUrl, - json: true, - ...options, + const opts: GotJSONOptions = { + prefixUrl: baseUrl, + json: options.body, + headers: options.headers, + ...options.options, + responseType: 'json', + context: { hostType, ...options.options?.context }, }; const method = opts.method || 'get'; if (method.toLowerCase() === 'post' && path === 'graphql') { // GitHub Enterprise uses unversioned graphql path - opts.baseUrl = opts.baseUrl.replace('/v3/', '/'); + opts.prefixUrl = opts.prefixUrl.toString().replace('/v3/', '/'); + delete opts.json; + opts.body = options.body as string; + } + // response expected as string + if (options.json === false) { + (opts as any).responseType = 'text'; } logger.trace(`${method.toUpperCase()} ${path}`); try { @@ -151,13 +157,13 @@ async function get( } } result = await got(path, opts); - if (opts.paginate) { + if (options.paginate) { // Check if result is paginated - const pageLimit = opts.pageLimit || 10; + const pageLimit = options.pageLimit || 10; const linkHeader = parseLinkHeader(result.headers.link as string); if (linkHeader && linkHeader.next && linkHeader.last) { let lastPage = +linkHeader.last.page; - if (!process.env.RENOVATE_PAGINATE_ALL && opts.paginate !== 'all') { + if (!process.env.RENOVATE_PAGINATE_ALL && options.paginate !== 'all') { lastPage = Math.min(pageLimit, lastPage); } const pageNumbers = Array.from( @@ -170,7 +176,7 @@ async function get( nextUrl.query.page = page.toString(); return get( URL.format(nextUrl), - { ...opts, paginate: false }, + { ...options, paginate: false }, okToRetry ); }); @@ -188,9 +194,13 @@ async function get( logger.debug('Recovered graphql query'); } } else if (okToRetry) { - logger.debug('Retrying graphql query'); - opts.body = opts.body.replace('first: 100', 'first: 25'); - return get(path, opts, !okToRetry); + logger.info('Retrying graphql query'); + // TODO: fix type + const body = (options.body as string).replace( + 'first: 100', + 'first: 25' + ); + return get(path, { ...options, body }, !okToRetry); } } } catch (gotErr) { @@ -199,16 +209,23 @@ async function get( return result; } -const helpers = ['get', 'post', 'put', 'patch', 'head', 'delete']; +const helpers: GotMethod[] = ['get', 'post', 'put', 'patch', 'head', 'delete']; -for (const x of helpers) { - (get as any)[x] = (url: string, opts: any): Promise => - get(url, { ...opts, method: x.toUpperCase() }); +for (const method of helpers) { + (get as any)[method] = (url: string, opts: any): Promise => + get(url, { ...opts, method }); } get.setBaseUrl = (u: string): void => { baseUrl = u; }; -export const api: GotApi = get as any; +export interface GithubApiOptions extends GotApiOptions { + /** + * response should be string instead of json + */ + json?: boolean; +} + +export const api: GotApi = get as any; export default api; diff --git a/lib/platform/github/gh-graphql-wrapper.ts b/lib/platform/github/gh-graphql-wrapper.ts index 1aab2f187cd316..77fb4ec7551682 100644 --- a/lib/platform/github/gh-graphql-wrapper.ts +++ b/lib/platform/github/gh-graphql-wrapper.ts @@ -5,7 +5,7 @@ import { getHostType, getBaseUrl, dispatchError } from './gh-got-wrapper'; const accept = 'application/vnd.github.merge-info-preview+json'; const gqlOpts: GotJSONOptions = { - json: true, + responseType: 'json', method: 'POST', headers: { accept, @@ -27,9 +27,9 @@ async function get( const path = 'graphql'; const options: GotJSONOptions = { ...gqlOpts, - hostType: getHostType(), - baseUrl: (getBaseUrl() || '').replace('/v3/', '/'), // GitHub Enterprise uses unversioned graphql path - body: { query }, + context: { hostType: getHostType() }, + prefixUrl: (getBaseUrl() || '').replace('/v3/', '/'), // GitHub Enterprise uses unversioned graphql path + json: { query }, }; if (global.appMode) { @@ -45,7 +45,7 @@ async function get( logger.trace(`Performing Github GraphQL request`); try { - const res = await got('graphql', options); + const res = await got('graphql', options); result = res && res.body; } catch (gotErr) { dispatchError(gotErr, path, options); diff --git a/lib/platform/github/index.ts b/lib/platform/github/index.ts index de873490641c24..477d91adf91804 100644 --- a/lib/platform/github/index.ts +++ b/lib/platform/github/index.ts @@ -4,7 +4,7 @@ import semver from 'semver'; import URL from 'url'; import { logger } from '../../logger'; -import { api } from './gh-got-wrapper'; +import { api, GithubApiOptions } from './gh-got-wrapper'; import * as hostRules from '../../util/host-rules'; import GitStorage, { StatusResult, CommitFilesConfig } from '../git/storage'; import { @@ -20,6 +20,7 @@ import { EnsureCommentConfig, EnsureIssueResult, BranchStatus, + GotApiOptions, } from '../common'; import { configFileNames } from '../../config/app-strings'; @@ -46,6 +47,7 @@ import { BRANCH_STATUS_PENDING, BRANCH_STATUS_SUCCESS, } from '../../constants/branch-constants'; +import { GotOptions } from '../../util/got'; const defaultConfigFile = configFileNames[0]; @@ -129,12 +131,10 @@ export async function initPlatform({ } let gitAuthor: string; let renovateUsername: string; + const options: GotOptions = { context: { token } }; try { - const userData = ( - await api.get(defaults.endpoint + 'user', { - token, - }) - ).body; + const userData = (await api.get(defaults.endpoint + 'user', { options })) + .body; renovateUsername = userData.login; gitAuthor = userData.name; } catch (err) { @@ -143,9 +143,7 @@ export async function initPlatform({ } try { const userEmail = ( - await api.get(defaults.endpoint + 'user/emails', { - token, - }) + await api.get(defaults.endpoint + 'user/emails', { options }) ).body; if (userEmail.length && userEmail[0].email) { gitAuthor += ` <${userEmail[0].email}>`; @@ -383,18 +381,18 @@ export async function initRepo({ // save parent name then delete config.parentRepo = config.repository; config.repository = null; + + const options: GotOptions = { context: { token: forkToken || opts.token } }; // Get list of existing repos const existingRepos = ( await api.get<{ full_name: string }[]>('user/repos?per_page=100', { - token: forkToken || opts.token, + options, paginate: true, }) ).body.map(r => r.full_name); try { config.repository = ( - await api.post(`repos/${repository}/forks`, { - token: forkToken || opts.token, - }) + await api.post(`repos/${repository}/forks`, { options }) ).body.full_name; } catch (err) /* istanbul ignore next */ { logger.debug({ err }, 'Error forking repository'); @@ -420,7 +418,7 @@ export async function initRepo({ sha: parentSha, force: true, }, - token: forkToken || opts.token, + options, } ); } catch (err) /* istanbul ignore next */ { @@ -640,7 +638,7 @@ async function getClosedPrs(): Promise { } } `; - const options = { + const options: GithubApiOptions = { body: JSON.stringify({ query }), json: false, }; @@ -744,7 +742,7 @@ async function getOpenPrs(): Promise { } } `; - const options = { + const options: GithubApiOptions = { headers, body: JSON.stringify({ query }), json: false, @@ -1117,7 +1115,7 @@ export async function getBranchStatus( const checkRunsUrl = `repos/${config.repository}/commits/${escapeHash( branchName )}/check-runs`; - const opts = { + const opts: GotApiOptions = { headers: { Accept: 'application/vnd.github.antiope-preview+json', }, @@ -1895,7 +1893,7 @@ export async function getVulnerabilityAlerts(): Promise { } } }`; - const options = { + const options: GithubApiOptions = { headers, body: JSON.stringify({ query }), json: false, diff --git a/lib/platform/gitlab/gl-got-wrapper.ts b/lib/platform/gitlab/gl-got-wrapper.ts index e98b1f9a607c1e..1472c17ddcfd65 100644 --- a/lib/platform/gitlab/gl-got-wrapper.ts +++ b/lib/platform/gitlab/gl-got-wrapper.ts @@ -1,7 +1,6 @@ import parseLinkHeader from 'parse-link-header'; - -import { GotApi, GotResponse } from '../common'; -import got from '../../util/got'; +import { GotApi, GotResponse, GotApiOptions } from '../common'; +import got, { GotJSONOptions, GotMethod } from '../../util/got'; import { logger } from '../../logger'; import { PLATFORM_FAILURE } from '../../constants/error-messages'; import { PLATFORM_TYPE_GITLAB } from '../../constants/platforms'; @@ -9,22 +8,25 @@ import { PLATFORM_TYPE_GITLAB } from '../../constants/platforms'; const hostType = PLATFORM_TYPE_GITLAB; let baseUrl = 'https://gitlab.com/api/v4/'; -async function get(path: string, options: any): Promise { - const opts = { - hostType, - baseUrl, - json: true, - ...options, +async function get(path: string, options: GotApiOptions): Promise { + const opts: GotJSONOptions = { + prefixUrl: baseUrl, + json: options.body, + headers: options.headers, + method: options.method, + ...options.options, + responseType: 'json', + context: { hostType, ...options.options?.context }, }; try { - const res = await got(path, opts); - if (opts.paginate) { + const res = await got(path, opts); + if (options.paginate) { // Check if result is paginated try { const linkHeader = parseLinkHeader(res.headers.link as string); if (linkHeader && linkHeader.next) { res.body = res.body.concat( - (await get(linkHeader.next.url, opts)).body + (await get(linkHeader.next.url, options)).body ); } } catch (err) /* istanbul ignore next */ { @@ -49,21 +51,13 @@ async function get(path: string, options: any): Promise { } } -const helpers = ['get', 'post', 'put', 'patch', 'head', 'delete']; - -interface GlGotApi - extends GotApi<{ - paginate?: boolean; - token?: string; - }> { - setBaseUrl(url: string): void; -} +const helpers: GotMethod[] = ['get', 'post', 'put', 'patch', 'head', 'delete']; -export const api: GlGotApi = {} as any; +export const api: GotApi = {} as any; -for (const x of helpers) { - (api as any)[x] = (url: string, opts: any): Promise => - get(url, { ...opts, method: x.toUpperCase() }); +for (const method of helpers) { + (api as any)[method] = (url: string, opts: any): Promise => + get(url, { ...opts, method }); } // eslint-disable-next-line @typescript-eslint/unbound-method diff --git a/lib/platform/gitlab/index.ts b/lib/platform/gitlab/index.ts index 49b159d9783357..d5fd7710b4e74d 100644 --- a/lib/platform/gitlab/index.ts +++ b/lib/platform/gitlab/index.ts @@ -83,7 +83,8 @@ export async function initPlatform({ } let gitAuthor: string; try { - const user = (await api.get(`user`, { token })).body; + const user = (await api.get(`user`, { options: { context: { token } } })) + .body; gitAuthor = `${user.name} <${user.email}>`; authorId = user.id; } catch (err) { diff --git a/lib/platform/index.ts b/lib/platform/index.ts index 38896f7b18f280..20187d41ce19f8 100644 --- a/lib/platform/index.ts +++ b/lib/platform/index.ts @@ -72,8 +72,7 @@ export async function initPlatform( name: gitAuthorParsed.name, email: gitAuthorParsed.address, }; - // TODO: types - const platformRule: any = { + const platformRule: hostRules.HostRule = { hostType: returnConfig.platform, hostName: URL.parse(returnConfig.endpoint).hostname, }; diff --git a/lib/util/got/auth.ts b/lib/util/got/auth.ts index df329ea12e699d..e5fdfe211a830b 100644 --- a/lib/util/got/auth.ts +++ b/lib/util/got/auth.ts @@ -1,35 +1,36 @@ +import got, { InitHook } from 'got'; +import { parse } from 'url'; import { logger } from '../../logger'; -import { create } from './util'; import { PLATFORM_TYPE_GITEA, PLATFORM_TYPE_GITHUB, PLATFORM_TYPE_GITLAB, } from '../../constants/platforms'; +import { RenovateGotExtendOptions } from './types'; -// istanbul ignore next -export default create({ - options: {}, - handler: (options, next) => { - if (options.auth || options.headers.authorization) { - return next(options); - } - if (options.token) { - logger.trace( - { hostname: options.hostname }, - 'Converting token to Bearer auth' - ); - if ( - options.hostType === PLATFORM_TYPE_GITHUB || - options.hostType === PLATFORM_TYPE_GITEA - ) { - options.headers.authorization = `token ${options.token}`; // eslint-disable-line no-param-reassign - } else if (options.hostType === PLATFORM_TYPE_GITLAB) { - options.headers['Private-token'] = options.token; // eslint-disable-line no-param-reassign - } else { - options.headers.authorization = `Bearer ${options.token}`; // eslint-disable-line no-param-reassign - } - delete options.token; // eslint-disable-line no-param-reassign +const hook: InitHook = (options: RenovateGotExtendOptions) => { + const uri = parse(options.url.toString()); + if (options.username || options.headers?.authorization) { + return; + } + if (options.context?.token) { + logger.trace({ hostname: uri.hostname }, 'Converting token to Bearer auth'); + if (!options.headers) options.headers = {}; // eslint-disable-line no-param-reassign + if ( + options.context?.hostType === PLATFORM_TYPE_GITHUB || + options.context?.hostType === PLATFORM_TYPE_GITEA + ) { + options.headers.authorization = `token ${options.context.token}`; // eslint-disable-line no-param-reassign + } else if (options.context.hostType === PLATFORM_TYPE_GITLAB) { + options.headers['Private-token'] = options.context?.token; // eslint-disable-line no-param-reassign + } else { + options.headers.authorization = `Bearer ${options.context.token}`; // eslint-disable-line no-param-reassign } - return next(options); - }, + delete options.context?.token; // eslint-disable-line no-param-reassign + } +}; + +// istanbul ignore next +export default got.extend({ + hooks: { init: [hook] }, }); diff --git a/lib/util/got/cache-get.ts b/lib/util/got/cache-get.ts index 60351f4d2155d9..c6b6f4e22e0bd7 100644 --- a/lib/util/got/cache-get.ts +++ b/lib/util/got/cache-get.ts @@ -1,36 +1,44 @@ +import got from 'got'; import crypto from 'crypto'; -import { create } from './util'; import { clone } from '../clone'; +import { RenovateGotNormalizedOptions } from './types'; // global.repoCache is reset to {} every time a repository is initialized // With this caching, it means every GET request is cached during each repository run // istanbul ignore next -export default create({ - options: {}, - handler: (options, next) => { - if (!global.repoCache) { - return next(options); - } - if (options.stream) { - return next(options); - } - if (options.method === 'GET') { - const cacheKey = crypto - .createHash('md5') - .update( - 'got-' + - JSON.stringify({ href: options.href, headers: options.headers }) - ) - .digest('hex'); - if (!global.repoCache[cacheKey] || options.useCache === false) { - global.repoCache[cacheKey] = next(options); +export default got.extend({ + handlers: [ + (options: RenovateGotNormalizedOptions, next) => { + if (!global.repoCache) { + return next(options); + } + if (options.isStream) { + return next(options); } - return global.repoCache[cacheKey].then(response => ({ - ...response, - body: clone(response.body), - })); - } - return next(options); - }, + if (options.method === 'GET') { + const cacheKey = crypto + .createHash('md5') + .update( + 'got-' + + JSON.stringify({ + href: options.url.href, + headers: options.headers, + }) + ) + .digest('hex'); + if ( + !global.repoCache[cacheKey] || + options.context?.useCache === false + ) { + global.repoCache[cacheKey] = next(options); + } + return global.repoCache[cacheKey].then(response => ({ + ...response, + body: clone(response.body), + })); + } + return next(options); + }, + ], }); diff --git a/lib/util/got/common.ts b/lib/util/got/common.ts deleted file mode 100644 index 579c9a88edf6ad..00000000000000 --- a/lib/util/got/common.ts +++ /dev/null @@ -1,32 +0,0 @@ -import got from 'got'; -import { Url } from 'url'; - -export interface Options { - hostType?: string; - search?: string; - useCache?: boolean; - readableHighWaterMark?: number; -} - -export type GotJSONOptions = Options & got.GotJSONOptions; -export type GotStreamOptions = Options & got.GotOptions; - -export type GotUrl = string | Url; - -export interface GotFn { - ( - url: GotUrl, - options?: GotJSONOptions - ): got.GotPromise; - - ( - url: GotUrl, - options?: Options & got.GotBodyOptions - ): got.GotPromise; -} - -export interface Got - extends GotFn, - Record<'get' | 'post' | 'put' | 'patch' | 'head' | 'delete', GotFn> { - stream(url: GotUrl, options?: GotStreamOptions): NodeJS.ReadableStream; -} diff --git a/lib/util/got/host-rules.ts b/lib/util/got/host-rules.ts index 75e72a76666e30..fa80e618854270 100644 --- a/lib/util/got/host-rules.ts +++ b/lib/util/got/host-rules.ts @@ -1,37 +1,43 @@ /* eslint-disable no-param-reassign */ +import got, { InitHook } from 'got'; +import { parse } from 'url'; import { logger } from '../../logger'; import * as hostRules from '../host-rules'; -import { create } from './util'; +import { RenovateGotExtendOptions } from './types'; // Apply host rules to requests +const hook: InitHook = (options: RenovateGotExtendOptions) => { + const uri = parse(options.url.toString()); + if (!uri.hostname) { + return; + } + const { username, password, token, timeout } = hostRules.find({ + hostType: options.context?.hostType, + url: options.url.toString(), + }); + if ( + options.headers?.authorization || + options.username || + options.context?.token + ) { + logger.trace('Authorization already set for host: ' + uri.hostname); + } else if (password) { + logger.trace('Applying Basic authentication for host ' + uri.hostname); + options.username = username; + options.password = password; + } else if (token) { + logger.trace('Applying Bearer authentication for host ' + uri.hostname); + options.context = { ...options.context, token }; + } + if (timeout) { + options.timeout = { request: timeout }; + } +}; + // istanbul ignore next -export default create({ - options: {}, - handler: (options, next) => { - if (!options.hostname) { - return next(options); - } - const { username, password, token, timeout } = hostRules.find({ - hostType: options.hostType, - url: options.href, - }); - if (options.headers.authorization || options.auth || options.token) { - logger.trace('Authorization already set for host: ' + options.hostname); - } else if (password) { - logger.trace( - 'Applying Basic authentication for host ' + options.hostname - ); - options.auth = `${username || ''}:${password}`; - } else if (token) { - logger.trace( - 'Applying Bearer authentication for host ' + options.hostname - ); - options.token = token; - } - if (timeout) { - options.gotTimeout = { request: timeout }; - } - return next(options); +export default got.extend({ + hooks: { + init: [hook], }, }); diff --git a/lib/util/got/index.ts b/lib/util/got/index.ts index 5bf9e47306fd70..61c7686e5019be 100644 --- a/lib/util/got/index.ts +++ b/lib/util/got/index.ts @@ -1,11 +1,11 @@ +import got from 'got'; import cacheGet from './cache-get'; import renovateAgent from './renovate-agent'; import hostRules from './host-rules'; import auth from './auth'; import { instance } from './stats'; -import { mergeInstances } from './util'; -export * from './common'; +export * from './types'; /* * This is the default got instance for Renovate. @@ -14,7 +14,7 @@ export * from './common'; * * Important: always put the renovateAgent one last, to make sure the correct user agent is used */ -export const api = mergeInstances( +export const api = got.extend( cacheGet, renovateAgent, hostRules, diff --git a/lib/util/got/stats.ts b/lib/util/got/stats.ts index 2217781fb2da45..03bc21572c2012 100644 --- a/lib/util/got/stats.ts +++ b/lib/util/got/stats.ts @@ -1,5 +1,5 @@ +import got, { CancelableRequest } from 'got'; import { logger } from '../../logger'; -import { create } from './util'; interface HostStats { median?: number; @@ -33,16 +33,26 @@ export const printStats = (): void => { logger.debug({ hostStats }, 'Host request stats (milliseconds)'); }; -export const instance = create({ - options: {}, - handler: (options, next) => { - const start = new Date(); - const nextPromise = next(options); - nextPromise.on('response', () => { - const elapsed = new Date().getTime() - start.getTime(); - stats[options.hostname] = stats[options.hostname] || []; - stats[options.hostname].push(elapsed); - }); - return nextPromise; - }, +export const instance = got.extend({ + handlers: [ + (options, next) => { + const nextPromise = next(options); + const { hostname } = options.url; + + (nextPromise as CancelableRequest).on( + 'response', + ({ timings }) => { + const elapsed = timings.response - timings.start; + + if (hostname in stats) { + stats[hostname].push(elapsed); + } else { + stats[hostname] = [elapsed]; + } + } + ); + + return nextPromise; + }, + ], }); diff --git a/lib/util/got/types.ts b/lib/util/got/types.ts new file mode 100644 index 00000000000000..a8c924ba626f40 --- /dev/null +++ b/lib/util/got/types.ts @@ -0,0 +1,66 @@ +import { + NormalizedOptions, + Headers, + Options, + Response as _Response, + GotError, + Method, + ExtendOptions, +} from 'got'; +import { Merge } from 'type-fest'; + +// TODO: remove when code is refactord +Object.defineProperty(GotError.prototype, 'statusCode', { + get: function statusCode(this: any) { + return this.response?.statusCode; + }, +}); +Object.defineProperty(GotError.prototype, 'code', { + get: function code(this: any) { + return this._code ?? this.response?.code; + }, + set: function code(v) { + this._code = v; + }, +}); + +export type GotResponse = _Response; + +export type GotMethod = Method; + +export type GotHeaders = Headers; + +export type RenovateGotContext = { + token?: string; + hostType?: string; + useCache?: boolean; +}; + +export type RenovateGotExtendOptions = Merge< + ExtendOptions, + { + context?: RenovateGotContext; + } +>; + +export type RenovateGotNormalizedOptions = Merge< + NormalizedOptions, + { + context: RenovateGotContext; + } +>; + +export type GotOptions = Merge< + Options, + { + context?: RenovateGotContext; + } +>; + +export type GotJSONOptions = Merge< + GotOptions, + { + responseType: 'json'; + readableHighWaterMark?: number; + } +>; diff --git a/lib/util/got/util.ts b/lib/util/got/util.ts deleted file mode 100644 index 8d476f54579fb2..00000000000000 --- a/lib/util/got/util.ts +++ /dev/null @@ -1,13 +0,0 @@ -import got from 'got'; -import { Got } from './common'; - -// TODO: missing types -export const mergeInstances = (got as any).mergeInstances as ( - ...args: got.GotInstance[] -) => Got; - -// TODO: missing types -export const create = (got as any).create as (defaults: { - options: any; - handler: Function; -}) => got.GotInstance; diff --git a/package.json b/package.json index f594f22d52f822..ba1a1e511dfb39 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,7 @@ "get-installed-path": "4.0.8", "github-url-from-git": "1.5.0", "global-agent": "2.1.8", - "got": "9.6.0", + "got": "10.6.0", "handlebars": "4.7.3", "hasha": "5.2.0", "ini": "1.3.5", @@ -181,7 +181,6 @@ "@types/fs-extra": "8.1.0", "@types/github-url-from-git": "1.5.0", "@types/global-agent": "2.1.0", - "@types/got": "9.6.9", "@types/ini": "1.3.30", "@types/jest": "25.1.3", "@types/js-yaml": "3.12.2", diff --git a/test/logger/__snapshots__/err-serializer.spec.ts.snap b/test/logger/__snapshots__/err-serializer.spec.ts.snap index fc37540fc11375..67d96f5c51e959 100644 --- a/test/logger/__snapshots__/err-serializer.spec.ts.snap +++ b/test/logger/__snapshots__/err-serializer.spec.ts.snap @@ -5,13 +5,12 @@ Object { "a": 1, "b": 2, "body": "some response body", - "gotOptions": Object { - "auth": "test:***********", + "message": "some message", + "options": Object { "headers": Object { "authorization": "** redacted **", }, }, - "message": "some message", "response": Object { "body": "some response body", }, diff --git a/test/logger/err-serializer.spec.ts b/test/logger/err-serializer.spec.ts index 429c858867c2fa..dba3632fe145c5 100644 --- a/test/logger/err-serializer.spec.ts +++ b/test/logger/err-serializer.spec.ts @@ -9,11 +9,10 @@ describe('logger/err-serializer', () => { response: { body: 'some response body', }, - gotOptions: { + options: { headers: { authorization: 'Bearer abc', }, - auth: 'test:token', }, }; expect(configSerializer(err)).toMatchSnapshot(); diff --git a/test/platform/bitbucket-server/__snapshots__/index.spec.ts.snap b/test/platform/bitbucket-server/__snapshots__/index.spec.ts.snap index 9e4d27570ea056..7521f29a492f60 100644 --- a/test/platform/bitbucket-server/__snapshots__/index.spec.ts.snap +++ b/test/platform/bitbucket-server/__snapshots__/index.spec.ts.snap @@ -2592,7 +2592,7 @@ Array [ exports[`platform/bitbucket-server endpoint with path initPlatform() should init 1`] = ` Object { - "endpoint": "https://stash.renovatebot.com/", + "endpoint": "https://stash.renovatebot.com/vcs/", } `; diff --git a/test/platform/bitbucket-server/index.spec.ts b/test/platform/bitbucket-server/index.spec.ts index 03bae0122044fd..0a2acdc987f2ae 100644 --- a/test/platform/bitbucket-server/index.spec.ts +++ b/test/platform/bitbucket-server/index.spec.ts @@ -12,6 +12,7 @@ import { BRANCH_STATUS_PENDING, BRANCH_STATUS_SUCCESS, } from '../../../lib/constants/branch-constants'; +import { GotJSONOptions } from '../../../lib/util/got'; describe('platform/bitbucket-server', () => { Object.entries(responses).forEach(([scenarioName, mockResponses]) => { @@ -28,16 +29,19 @@ describe('platform/bitbucket-server', () => { jest.mock('delay'); jest.mock( '../../../lib/util/got', - () => (url: string, options: { method: string }) => { - const { method } = options; - const body = mockResponses[url] && mockResponses[url][method]; + () => async (path: string, options: GotJSONOptions) => { + const { method, prefixUrl } = options; + const { resolve } = await import('url'); + const url = resolve(prefixUrl.toString(), path); + const body = + mockResponses[url] && mockResponses[url][method.toUpperCase()]; if (!body) { - return Promise.reject(new Error(`no match for ${method} ${url}`)); + throw new Error(`no match for ${method} ${url}`); } if (body instanceof Promise) { return body; } - return Promise.resolve({ body }); + return { body }; } ); jest.mock('../../../lib/platform/git/storage'); @@ -73,10 +77,7 @@ describe('platform/bitbucket-server', () => { ), } as any) ); - const endpoint = - scenarioName === 'endpoint with path' - ? 'https://stash.renovatebot.com/vcs/' - : 'https://stash.renovatebot.com'; + const endpoint = mockResponses.baseURL; hostRules.find.mockReturnValue({ username: 'abc', password: '123', @@ -94,7 +95,7 @@ describe('platform/bitbucket-server', () => { function initRepo(config?: Partial) { return bitbucket.initRepo({ - endpoint: 'https://stash.renovatebot.com/vcs/', + endpoint: mockResponses.baseURL, repository: 'SOME/repo', ...config, } as any); @@ -114,7 +115,7 @@ describe('platform/bitbucket-server', () => { it('should init', async () => { expect( await bitbucket.initPlatform({ - endpoint: 'https://stash.renovatebot.com', + endpoint: mockResponses.baseURL, username: 'abc', password: '123', }) diff --git a/test/platform/gitea/gitea-got-wrapper.spec.ts b/test/platform/gitea/gitea-got-wrapper.spec.ts index bd1f5d1dfdbe0f..8368becf65a634 100644 --- a/test/platform/gitea/gitea-got-wrapper.spec.ts +++ b/test/platform/gitea/gitea-got-wrapper.spec.ts @@ -1,11 +1,11 @@ +import { Got } from 'got'; import { GotResponse } from '../../../lib/platform'; import { partial } from '../../util'; -import { GotFn } from '../../../lib/util/got'; import { GiteaGotApi } from '../../../lib/platform/gitea/gitea-got-wrapper'; describe('platform/gitea/gitea-got-wrapper', () => { let api: GiteaGotApi; - let got: jest.Mocked & jest.Mock; + let got: jest.Mocked & jest.Mock; const baseURL = 'https://gitea.renovatebot.com/api/v1'; @@ -13,8 +13,7 @@ describe('platform/gitea/gitea-got-wrapper', () => { jest.resetAllMocks(); jest.mock('../../../lib/util/got'); - api = (await import('../../../lib/platform/gitea/gitea-got-wrapper')) - .api as any; + api = (await import('../../../lib/platform/gitea/gitea-got-wrapper')).api; got = (await import('../../../lib/util/got')).api as any; api.setBaseUrl(baseURL); }); diff --git a/test/platform/gitea/gitea-helper.spec.ts b/test/platform/gitea/gitea-helper.spec.ts index 5ff1cf830a0fbc..fb01a83b389162 100644 --- a/test/platform/gitea/gitea-helper.spec.ts +++ b/test/platform/gitea/gitea-helper.spec.ts @@ -3,7 +3,7 @@ import { GotResponse } from '../../../lib/platform'; import { partial } from '../../util'; import { GiteaGotApi, - GiteaGotOptions, + GiteaApiOptions, } from '../../../lib/platform/gitea/gitea-got-wrapper'; import * as ght from '../../../lib/platform/gitea/gitea-helper'; import { PRSearchParams } from '../../../lib/platform/gitea/gitea-helper'; @@ -161,7 +161,7 @@ describe('platform/gitea/gitea-helper', () => { // Mock request implementation once and verify request api[options.method].mockImplementationOnce( - (rawUrl: string, apiOpts?: GiteaGotOptions): Promise> => { + (rawUrl: string, apiOpts?: GiteaApiOptions): Promise> => { // Construct and parse absolute URL const absoluteUrl = rawUrl.includes('://') ? rawUrl diff --git a/test/platform/github/__snapshots__/index.spec.ts.snap b/test/platform/github/__snapshots__/index.spec.ts.snap index d95c4fbf08ae40..8d527c6afdeaae 100644 --- a/test/platform/github/__snapshots__/index.spec.ts.snap +++ b/test/platform/github/__snapshots__/index.spec.ts.snap @@ -200,8 +200,12 @@ Array [ Array [ "user/repos?per_page=100", Object { + "options": Object { + "context": Object { + "token": "abc123", + }, + }, "paginate": true, - "token": "abc123", }, ], Array [ diff --git a/test/workers/repository/process/lookup/index.spec.ts b/test/workers/repository/process/lookup/index.spec.ts index 9b74280fd8c8ad..1c5772e3c076b1 100644 --- a/test/workers/repository/process/lookup/index.spec.ts +++ b/test/workers/repository/process/lookup/index.spec.ts @@ -38,6 +38,7 @@ describe('workers/repository/process/lookup', () => { config.rangeStrategy = 'replace'; global.repoCache = {}; jest.resetAllMocks(); + nock.cleanAll(); }); describe('.lookupUpdates()', () => { diff --git a/yarn.lock b/yarn.lock index 1117eedea98099..36a74e68f6719d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1214,10 +1214,10 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-1.2.0.tgz#63ce3638cb85231f3704164c90a18ef816da3fb7" integrity sha512-mwhXGkRV5dlvQc4EgPDxDxO6WuMBVymGFd1CA+2Y+z5dG9MNspoQ+AWjl/Ld1MnpCL8AKbosZlDVohqcIwuWsw== -"@sindresorhus/is@^0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" - integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== +"@sindresorhus/is@^2.0.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-2.1.0.tgz#6ad4ca610f696098e92954ab431ff83bea0ce13f" + integrity sha512-lXKXfypKo644k4Da4yXkPCrwcvn6SlUW2X2zFbuflKHNjf0w9htru01bo26uMhleMXsDmnZ12eJLdrAZa9MANg== "@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0", "@sinonjs/commons@^1.7.0": version "1.7.0" @@ -1255,12 +1255,12 @@ dependencies: lodash "^4.17.14" -"@szmarczak/http-timer@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" - integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== +"@szmarczak/http-timer@^4.0.0": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152" + integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ== dependencies: - defer-to-connect "^1.0.1" + defer-to-connect "^2.0.0" "@types/babel__core@^7.1.0": version "7.1.3" @@ -1309,6 +1309,16 @@ dependencies: "@types/node" "*" +"@types/cacheable-request@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976" + integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "*" + "@types/node" "*" + "@types/responselike" "*" + "@types/chai@4.2.9": version "4.2.9" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.9.tgz#194332625ed2ae914aef00b8d5ca3b77e7924cc6" @@ -1365,14 +1375,10 @@ resolved "https://registry.yarnpkg.com/@types/global-agent/-/global-agent-2.1.0.tgz#1becbcb067c9c7a8d2cd70b786ca1d062538e299" integrity sha512-xBOerse4Agekl7VZJclA9bfuA9aa3u9T24TDkBiMQrZgu4qe5HMBPzVGzAt2k4dx/v3uIFI6CzG0Z9X894LHrg== -"@types/got@9.6.9": - version "9.6.9" - resolved "https://registry.yarnpkg.com/@types/got/-/got-9.6.9.tgz#b3192188b96c871b9c67dc80d1b0336441432a38" - integrity sha512-w+ZE+Ovp6fM+1sHwJB7RN3f3pTJHZkyABuULqbtknqezQyWadFEp5BzOXaZzRqAw2md6/d3ybxQJt+BNgpvzOg== - dependencies: - "@types/node" "*" - "@types/tough-cookie" "*" - form-data "^2.5.0" +"@types/http-cache-semantics@*": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a" + integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== "@types/ini@1.3.30": version "1.3.30" @@ -1422,6 +1428,13 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.30.tgz#44cb52f32a809734ca562e685c6473b5754a7818" integrity sha512-sqm9g7mHlPY/43fcSNrCYfOeX9zkTTK+euO5E6+CVijSMm5tTjkVdwdqRkY3ljjIAf8679vps5jKUoJBCLsMDA== +"@types/keyv@*": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" + integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw== + dependencies: + "@types/node" "*" + "@types/later@1.2.6": version "1.2.6" resolved "https://registry.yarnpkg.com/@types/later/-/later-1.2.6.tgz#8ea70d292e5fb880039a7ed13da19a24c9e35a56" @@ -1503,6 +1516,13 @@ resolved "https://registry.yarnpkg.com/@types/registry-auth-token/-/registry-auth-token-3.3.0.tgz#bfb57ed386d84749c982ec20c804ac119382b285" integrity sha512-HEnnGNRFbVXwqLV1xzQTY4zElLEn85mHAXm9LbAut0+gQI6LafAhW0qmCLuBcm5QtekXQWXFGchi0ZjTdX6Upw== +"@types/responselike@*": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + "@types/retry@^0.12.0": version "0.12.0" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" @@ -1548,11 +1568,6 @@ resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.1.0.tgz#19cf73a7bcf641965485119726397a096f0049bd" integrity sha512-6IwZ9HzWbCq6XoQWhxLpDjuADodH/MKXRUIDFudvgjcVdjFknvmR+DNsoUeer4XPrEnrZs04Jj+kfV9pFsrhmA== -"@types/tough-cookie@*": - version "2.3.5" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.5.tgz#9da44ed75571999b65c37b60c9b2b88db54c585d" - integrity sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg== - "@types/traverse@0.6.32": version "0.6.32" resolved "https://registry.yarnpkg.com/@types/traverse/-/traverse-0.6.32.tgz#f9fdfa40cd4898deaa975a14511aec731de8235e" @@ -2312,18 +2327,25 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" -cacheable-request@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" - integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== +cacheable-lookup@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-2.0.0.tgz#33b1e56f17507f5cf9bb46075112d65473fb7713" + integrity sha512-s2piO6LvA7xnL1AR03wuEdSx3BZT3tIJpZ56/lcJwzO/6DTJZlTs7X3lrvPxk6d1PlDe6PrVe2TjlUIZNFglAQ== + dependencies: + keyv "^4.0.0" + +cacheable-request@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.1.tgz#062031c2856232782ed694a257fa35da93942a58" + integrity sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw== dependencies: clone-response "^1.0.2" get-stream "^5.1.0" http-cache-semantics "^4.0.0" - keyv "^3.0.0" + keyv "^4.0.0" lowercase-keys "^2.0.0" normalize-url "^4.1.0" - responselike "^1.0.2" + responselike "^2.0.0" call-limit@^1.1.1: version "1.1.1" @@ -3033,12 +3055,12 @@ decode-uri-component@^0.2.0: resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= -decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= +decompress-response@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-5.0.0.tgz#7849396e80e3d1eba8cb2f75ef4930f76461cb0f" + integrity sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw== dependencies: - mimic-response "^1.0.0" + mimic-response "^2.0.0" deep-extend@^0.6.0: version "0.6.0" @@ -3062,10 +3084,10 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -defer-to-connect@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.0.tgz#b41bd7efa8508cef13f8456975f7a278c72833fd" - integrity sha512-WE2sZoctWm/v4smfCAdjYbrfS55JiMRdlY9ZubFhsYbteCK9+BvAx4YV7nPjYM6ZnX5BcoVKwfmyx9sIFTgQMQ== +defer-to-connect@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.0.tgz#83d6b199db041593ac84d781b5222308ccf4c2c1" + integrity sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg== define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" @@ -3899,15 +3921,6 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= -form-data@^2.5.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" - integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -4248,22 +4261,26 @@ globby@^10.0.0: merge2 "^1.2.3" slash "^3.0.0" -got@9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" - integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== - dependencies: - "@sindresorhus/is" "^0.14.0" - "@szmarczak/http-timer" "^1.1.2" - cacheable-request "^6.0.0" - decompress-response "^3.3.0" +got@10.6.0: + version "10.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-10.6.0.tgz#ac3876261a4d8e5fc4f81186f79955ce7b0501dc" + integrity sha512-3LIdJNTdCFbbJc+h/EH0V5lpNpbJ6Bfwykk21lcQvQsEcrzdi/ltCyQehFHLzJ/ka0UMH4Slg0hkYvAZN9qUDg== + dependencies: + "@sindresorhus/is" "^2.0.0" + "@szmarczak/http-timer" "^4.0.0" + "@types/cacheable-request" "^6.0.1" + cacheable-lookup "^2.0.0" + cacheable-request "^7.0.1" + decompress-response "^5.0.0" duplexer3 "^0.1.4" - get-stream "^4.1.0" - lowercase-keys "^1.0.1" - mimic-response "^1.0.1" - p-cancelable "^1.0.0" - to-readable-stream "^1.0.0" - url-parse-lax "^3.0.0" + get-stream "^5.0.0" + lowercase-keys "^2.0.0" + mimic-response "^2.1.0" + p-cancelable "^2.0.0" + p-event "^4.0.0" + responselike "^2.0.0" + to-readable-stream "^2.0.0" + type-fest "^0.10.0" got@^6.7.1: version "6.7.1" @@ -5575,10 +5592,10 @@ jsesc@~0.5.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= -json-buffer@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" - integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== json-dup-key-validator@1.0.2: version "1.0.2" @@ -5651,12 +5668,12 @@ just-extend@^4.0.2: resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.0.2.tgz#f3f47f7dfca0f989c55410a7ebc8854b07108afc" integrity sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw== -keyv@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" - integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== +keyv@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.0.tgz#2d1dab694926b2d427e4c74804a10850be44c12f" + integrity sha512-U7ioE8AimvRVLfw4LffyOIRhL2xVgmE8T22L6i0BucSnBUyv4w+I7VN/zVZwRKHOI6ZRUcdMdWHQ8KSUvGpEog== dependencies: - json-buffer "3.0.0" + json-buffer "3.0.1" kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" @@ -6119,7 +6136,7 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" -lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: +lowercase-keys@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== @@ -6383,11 +6400,21 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -mimic-response@^1.0.0, mimic-response@^1.0.1: +mimic-response@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== +mimic-response@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.0.0.tgz#996a51c60adf12cb8a87d7fb8ef24c2f3d5ebb46" + integrity sha512-8ilDoEapqA4uQ3TwS0jakGONKXVJqpy+RpM+3b7pLdOjghCrEiGp9SRkFbUHAmZW9vdnrENWHjaweIoTIJExSQ== + +mimic-response@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" + integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== + "minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.3, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -7241,16 +7268,23 @@ p-all@2.1.0: dependencies: p-map "^2.0.0" -p-cancelable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" - integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== +p-cancelable@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.0.0.tgz#4a3740f5bdaf5ed5d7c3e34882c6fb5d6b266a6e" + integrity sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg== p-each-series@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.1.0.tgz#961c8dd3f195ea96c747e636b262b800a6b1af48" integrity sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ== +p-event@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-event/-/p-event-4.1.0.tgz#e92bb866d7e8e5b732293b1c8269d38e9982bf8e" + integrity sha512-4vAd06GCsgflX4wHN1JqrMzBh/8QZ4j+rzp0cd2scXRwuBEv+QR3wrVA5aLhWDLw4y2WgDKvzWF3CCLmVM1UgA== + dependencies: + p-timeout "^2.0.1" + p-filter@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-filter/-/p-filter-2.1.0.tgz#1b1472562ae7a0f742f0f3d3d3718ea66ff9c09c" @@ -7333,6 +7367,13 @@ p-retry@^4.0.0: "@types/retry" "^0.12.0" retry "^0.12.0" +p-timeout@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" + integrity sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA== + dependencies: + p-finally "^1.0.0" + p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -7633,11 +7674,6 @@ prepend-http@^1.0.1: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" - integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= - prettier@1.19.1: version "1.19.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" @@ -8319,12 +8355,12 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.3. dependencies: path-parse "^1.0.6" -responselike@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" - integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= +responselike@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" + integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== dependencies: - lowercase-keys "^1.0.0" + lowercase-keys "^2.0.0" restore-cursor@^3.1.0: version "3.1.0" @@ -9332,10 +9368,10 @@ to-object-path@^0.3.0: dependencies: kind-of "^3.0.2" -to-readable-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" - integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== +to-readable-stream@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-2.1.0.tgz#82880316121bea662cdc226adb30addb50cb06e8" + integrity sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w== to-regex-range@^2.1.0: version "2.1.1" @@ -9489,6 +9525,11 @@ type-fest@0.11.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== +type-fest@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.10.0.tgz#7f06b2b9fbfc581068d1341ffabd0349ceafc642" + integrity sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw== + type-fest@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1" @@ -9737,13 +9778,6 @@ url-parse-lax@^1.0.0: dependencies: prepend-http "^1.0.1" -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" - integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= - dependencies: - prepend-http "^2.0.0" - url@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64"