renovate 44.198.0 → 44.198.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/modules/datasource/apk/index.js +6 -3
  2. package/dist/modules/datasource/apk/index.js.map +1 -1
  3. package/dist/modules/datasource/azure-bicep-resource/index.js +2 -2
  4. package/dist/modules/datasource/azure-bicep-resource/index.js.map +1 -1
  5. package/dist/modules/datasource/buildpacks-registry/index.js +1 -1
  6. package/dist/modules/datasource/buildpacks-registry/index.js.map +1 -1
  7. package/dist/modules/datasource/cdnjs/index.js +2 -2
  8. package/dist/modules/datasource/cdnjs/index.js.map +1 -1
  9. package/dist/modules/datasource/cpan/index.js +1 -1
  10. package/dist/modules/datasource/cpan/index.js.map +1 -1
  11. package/dist/modules/datasource/crate/index.js +3 -3
  12. package/dist/modules/datasource/crate/index.js.map +1 -1
  13. package/dist/modules/datasource/datasource.js.map +1 -1
  14. package/dist/modules/datasource/docker/index.js +14 -8
  15. package/dist/modules/datasource/docker/index.js.map +1 -1
  16. package/dist/modules/datasource/dotnet-version/index.js +2 -2
  17. package/dist/modules/datasource/dotnet-version/index.js.map +1 -1
  18. package/dist/modules/datasource/elm-package/index.js +1 -1
  19. package/dist/modules/datasource/elm-package/index.js.map +1 -1
  20. package/dist/modules/datasource/galaxy/index.js +1 -1
  21. package/dist/modules/datasource/galaxy/index.js.map +1 -1
  22. package/dist/modules/datasource/git-host-tags.js +6 -10
  23. package/dist/modules/datasource/git-host-tags.js.map +1 -1
  24. package/dist/modules/datasource/gitea-tags/base.js +2 -4
  25. package/dist/modules/datasource/gitea-tags/base.js.map +1 -1
  26. package/dist/modules/datasource/go/common.js +2 -2
  27. package/dist/modules/datasource/go/common.js.map +1 -1
  28. package/dist/modules/datasource/go/index.js +2 -2
  29. package/dist/modules/datasource/go/index.js.map +1 -1
  30. package/dist/modules/datasource/go/releases-direct.js +1 -1
  31. package/dist/modules/datasource/go/releases-direct.js.map +1 -1
  32. package/dist/modules/datasource/go/releases-goproxy.js +2 -2
  33. package/dist/modules/datasource/go/releases-goproxy.js.map +1 -1
  34. package/dist/modules/datasource/hex/index.js +1 -1
  35. package/dist/modules/datasource/hex/index.js.map +1 -1
  36. package/dist/modules/datasource/java-version/index.js +1 -1
  37. package/dist/modules/datasource/java-version/index.js.map +1 -1
  38. package/dist/modules/datasource/jsr/index.js +1 -1
  39. package/dist/modules/datasource/jsr/index.js.map +1 -1
  40. package/dist/modules/datasource/packagist/index.js +1 -1
  41. package/dist/modules/datasource/packagist/index.js.map +1 -1
  42. package/dist/modules/datasource/ruby-version/index.js +1 -1
  43. package/dist/modules/datasource/ruby-version/index.js.map +1 -1
  44. package/dist/modules/datasource/rubygems/index.js +1 -1
  45. package/dist/modules/datasource/rubygems/index.js.map +1 -1
  46. package/dist/modules/datasource/rust-version/index.js +1 -1
  47. package/dist/modules/datasource/rust-version/index.js.map +1 -1
  48. package/dist/modules/datasource/typst/index.js +1 -1
  49. package/dist/modules/datasource/typst/index.js.map +1 -1
  50. package/dist/util/cache/package/with-cache.js +15 -11
  51. package/dist/util/cache/package/with-cache.js.map +1 -1
  52. package/package.json +1 -1
  53. package/renovate-schema.json +2 -2
@@ -124,9 +124,12 @@ var ApkDatasource = class ApkDatasource extends Datasource {
124
124
  namespace: `datasource-${ApkDatasource.id}`,
125
125
  key: componentUrl,
126
126
  ttlMinutes: 60,
127
- fallback: true,
128
- shouldCacheResult: isNonEmptyObject
129
- }, () => this._getPackages(componentUrl));
127
+ fallback: true
128
+ }, async (ctx) => {
129
+ const packages = await this._getPackages(componentUrl);
130
+ ctx.cacheable = isNonEmptyObject(packages);
131
+ return packages;
132
+ });
130
133
  }
131
134
  /**
132
135
  * Gets releases for a specific package from APK repositories
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["fs.ensureCacheDir","fs.createCacheWriteStream","fs.pipeline","tarExtract","fs.cachePathExists","HttpError","fs.rmCache"],"sources":["../../../../lib/modules/datasource/apk/index.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { isNonEmptyObject } from '@sindresorhus/is';\nimport { extract as tarExtract } from 'tar';\nimport upath from 'upath';\nimport { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport * as fs from '../../../util/fs/index.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport { joinUrlParts } from '../../../util/url.ts';\nimport { id as apkVersioning } from '../../versioning/apk/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, Release, ReleaseResult } from '../types.ts';\nimport { parseApkIndexFile } from './parser.ts';\nimport type { ApkPackage } from './types.ts';\nimport { constructComponentUrls } from './url.ts';\n\nexport const apkDatasourceId = 'apk';\n\nconst defaultConfig = {\n commitMessageTopic: '{{{depName}}} APK package',\n commitMessageExtra:\n 'to {{#if isMajor}}{{{prettyNewMajor}}}{{else}}{{{prettyNewVersion}}}{{/if}}',\n};\n\n/**\n * Groups packages by name so that a lookup does not scan the whole index.\n *\n * An index holds thousands of packages, and a name can appear more than once\n * when the component serves multiple versions of it.\n */\nfunction groupPackagesByName(\n packages: ApkPackage[],\n): Record<string, ApkPackage[]> {\n const packagesByName: Record<string, ApkPackage[]> = {};\n\n for (const pkg of packages) {\n packagesByName[pkg.name] ??= [];\n packagesByName[pkg.name].push(pkg);\n }\n\n return packagesByName;\n}\n\nexport class ApkDatasource extends Datasource {\n static readonly id = apkDatasourceId;\n\n override readonly defaultVersioning = apkVersioning;\n\n /**\n * Alpine APK repositories are laid out as\n * `{base}/{branch}/{component}/{arch}/APKINDEX.tar.gz`, and `/etc/apk/repositories`\n * holds one entry per component.\n *\n * For Renovate, the path segments are encoded as query parameters so that a single\n * registry URL can cover multiple components.\n *\n * The following query parameter is required:\n * - arch: e.g. x86_64, aarch64, armv7\n *\n * The following query parameters are optional, as repositories such as Wolfi serve\n * their index directly below the repository root:\n * - branch: latest-stable, v3.19, edge or any other Alpine branch\n * - components: comma separated list of components, e.g. main,community,testing\n */\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return [\n 'https://dl-cdn.alpinelinux.org/alpine?branch=latest-stable&components=main&arch=x86_64',\n ];\n }\n\n override readonly defaultConfig = defaultConfig;\n\n override supportsCustomRegistry(_packageName: string): boolean {\n return true;\n }\n\n override readonly registryStrategy = 'merge';\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `buildDate` field in the results.';\n\n constructor() {\n super(ApkDatasource.id);\n }\n\n /**\n * Gets all available packages from a single APK component, keyed by package name\n */\n private async _getPackages(\n componentUrl: string,\n ): Promise<Record<string, ApkPackage[]>> {\n logger.debug(`Fetching APK packages from ${componentUrl}`);\n\n const extractId = randomUUID();\n const cacheDir = await fs.ensureCacheDir(upath.join('apk', extractId));\n const tarFile = upath.join(cacheDir, 'APKINDEX.tar.gz');\n const extractedFile = upath.join(cacheDir, 'APKINDEX');\n\n try {\n const indexUrl = joinUrlParts(componentUrl, 'APKINDEX.tar.gz');\n logger.debug(`Attempting to download ${indexUrl}`);\n const readStream = this.http.stream(indexUrl);\n const writeStream = fs.createCacheWriteStream(tarFile);\n await fs.pipeline(readStream, writeStream);\n\n await tarExtract({\n file: tarFile,\n cwd: cacheDir,\n filter: (path) => upath.basename(path) === 'APKINDEX',\n });\n\n if (!(await fs.cachePathExists(extractedFile))) {\n logger.warn({ componentUrl }, 'APKINDEX file not found in tar archive');\n return {};\n }\n\n logger.debug('Successfully extracted APKINDEX content');\n\n let packages: ApkPackage[] = [];\n try {\n packages = await parseApkIndexFile(extractedFile);\n } catch (err) {\n logger.warn({ componentUrl, err }, 'Error parsing APK index file');\n return {};\n }\n\n logger.debug(\n { componentUrl, packageCount: packages.length },\n 'Successfully parsed APK index',\n );\n\n return groupPackagesByName(packages);\n } catch (err) {\n if (err instanceof HttpError) {\n const statusCode = err.response?.statusCode;\n if (statusCode === 429 || (statusCode && statusCode >= 500)) {\n throw new ExternalHostError(err);\n }\n // The caller logs the failure once for the component it was skipping\n throw err;\n }\n\n logger.warn(\n { componentUrl, err },\n 'Error extracting APK index from tar.gz',\n );\n return {};\n } finally {\n await fs.rmCache(cacheDir);\n }\n }\n\n private getPackages(\n componentUrl: string,\n ): Promise<Record<string, ApkPackage[]>> {\n return withCache(\n {\n namespace: `datasource-${ApkDatasource.id}`,\n key: componentUrl,\n ttlMinutes: 60,\n fallback: true,\n // Soft failures resolve to an empty index, which must not be cached as a valid one\n shouldCacheResult: isNonEmptyObject,\n },\n () => this._getPackages(componentUrl),\n );\n }\n\n /**\n * Gets releases for a specific package from APK repositories\n */\n async getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore if -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n logger.debug(`Getting APK releases for ${packageName} from ${registryUrl}`);\n\n const componentUrls = constructComponentUrls(registryUrl);\n let result: ReleaseResult | null = null;\n // A package can be served by more than one component, so the same version\n // must not be reported twice\n const seenVersions = new Set<string>();\n\n for (const componentUrl of componentUrls) {\n try {\n const packages = await this.getPackages(componentUrl);\n const matchingPackages = packages[packageName];\n\n if (!matchingPackages) {\n logger.debug(\n { packageName, componentUrl },\n 'No matching packages found',\n );\n continue;\n }\n\n const releases: Release[] = [];\n for (const pkg of matchingPackages) {\n if (seenVersions.has(pkg.version)) {\n continue;\n }\n seenVersions.add(pkg.version);\n releases.push({\n version: pkg.version,\n releaseTimestamp: pkg.buildDate\n ? asTimestamp(pkg.buildDate * 1000)\n : undefined,\n });\n }\n\n logger.trace(\n {\n packageName,\n componentUrl,\n releaseCount: releases.length,\n releases,\n },\n 'Found APK releases',\n );\n\n result ??= { releases: [], registryUrl };\n result.homepage ??= matchingPackages.find((pkg) => pkg.url)?.url;\n result.releases.push(...releases);\n } catch (err) {\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug(\n { packageName, componentUrl, err },\n 'Skipping APK component due to an error',\n );\n }\n }\n\n return result;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["fs.ensureCacheDir","fs.createCacheWriteStream","fs.pipeline","tarExtract","fs.cachePathExists","HttpError","fs.rmCache"],"sources":["../../../../lib/modules/datasource/apk/index.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { isNonEmptyObject } from '@sindresorhus/is';\nimport { extract as tarExtract } from 'tar';\nimport upath from 'upath';\nimport { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport * as fs from '../../../util/fs/index.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport { joinUrlParts } from '../../../util/url.ts';\nimport { id as apkVersioning } from '../../versioning/apk/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, Release, ReleaseResult } from '../types.ts';\nimport { parseApkIndexFile } from './parser.ts';\nimport type { ApkPackage } from './types.ts';\nimport { constructComponentUrls } from './url.ts';\n\nexport const apkDatasourceId = 'apk';\n\nconst defaultConfig = {\n commitMessageTopic: '{{{depName}}} APK package',\n commitMessageExtra:\n 'to {{#if isMajor}}{{{prettyNewMajor}}}{{else}}{{{prettyNewVersion}}}{{/if}}',\n};\n\n/**\n * Groups packages by name so that a lookup does not scan the whole index.\n *\n * An index holds thousands of packages, and a name can appear more than once\n * when the component serves multiple versions of it.\n */\nfunction groupPackagesByName(\n packages: ApkPackage[],\n): Record<string, ApkPackage[]> {\n const packagesByName: Record<string, ApkPackage[]> = {};\n\n for (const pkg of packages) {\n packagesByName[pkg.name] ??= [];\n packagesByName[pkg.name].push(pkg);\n }\n\n return packagesByName;\n}\n\nexport class ApkDatasource extends Datasource {\n static readonly id = apkDatasourceId;\n\n override readonly defaultVersioning = apkVersioning;\n\n /**\n * Alpine APK repositories are laid out as\n * `{base}/{branch}/{component}/{arch}/APKINDEX.tar.gz`, and `/etc/apk/repositories`\n * holds one entry per component.\n *\n * For Renovate, the path segments are encoded as query parameters so that a single\n * registry URL can cover multiple components.\n *\n * The following query parameter is required:\n * - arch: e.g. x86_64, aarch64, armv7\n *\n * The following query parameters are optional, as repositories such as Wolfi serve\n * their index directly below the repository root:\n * - branch: latest-stable, v3.19, edge or any other Alpine branch\n * - components: comma separated list of components, e.g. main,community,testing\n */\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return [\n 'https://dl-cdn.alpinelinux.org/alpine?branch=latest-stable&components=main&arch=x86_64',\n ];\n }\n\n override readonly defaultConfig = defaultConfig;\n\n override supportsCustomRegistry(_packageName: string): boolean {\n return true;\n }\n\n override readonly registryStrategy = 'merge';\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `buildDate` field in the results.';\n\n constructor() {\n super(ApkDatasource.id);\n }\n\n /**\n * Gets all available packages from a single APK component, keyed by package name\n */\n private async _getPackages(\n componentUrl: string,\n ): Promise<Record<string, ApkPackage[]>> {\n logger.debug(`Fetching APK packages from ${componentUrl}`);\n\n const extractId = randomUUID();\n const cacheDir = await fs.ensureCacheDir(upath.join('apk', extractId));\n const tarFile = upath.join(cacheDir, 'APKINDEX.tar.gz');\n const extractedFile = upath.join(cacheDir, 'APKINDEX');\n\n try {\n const indexUrl = joinUrlParts(componentUrl, 'APKINDEX.tar.gz');\n logger.debug(`Attempting to download ${indexUrl}`);\n const readStream = this.http.stream(indexUrl);\n const writeStream = fs.createCacheWriteStream(tarFile);\n await fs.pipeline(readStream, writeStream);\n\n await tarExtract({\n file: tarFile,\n cwd: cacheDir,\n filter: (path) => upath.basename(path) === 'APKINDEX',\n });\n\n if (!(await fs.cachePathExists(extractedFile))) {\n logger.warn({ componentUrl }, 'APKINDEX file not found in tar archive');\n return {};\n }\n\n logger.debug('Successfully extracted APKINDEX content');\n\n let packages: ApkPackage[] = [];\n try {\n packages = await parseApkIndexFile(extractedFile);\n } catch (err) {\n logger.warn({ componentUrl, err }, 'Error parsing APK index file');\n return {};\n }\n\n logger.debug(\n { componentUrl, packageCount: packages.length },\n 'Successfully parsed APK index',\n );\n\n return groupPackagesByName(packages);\n } catch (err) {\n if (err instanceof HttpError) {\n const statusCode = err.response?.statusCode;\n if (statusCode === 429 || (statusCode && statusCode >= 500)) {\n throw new ExternalHostError(err);\n }\n // The caller logs the failure once for the component it was skipping\n throw err;\n }\n\n logger.warn(\n { componentUrl, err },\n 'Error extracting APK index from tar.gz',\n );\n return {};\n } finally {\n await fs.rmCache(cacheDir);\n }\n }\n\n private getPackages(\n componentUrl: string,\n ): Promise<Record<string, ApkPackage[]>> {\n return withCache(\n {\n namespace: `datasource-${ApkDatasource.id}`,\n key: componentUrl,\n ttlMinutes: 60,\n fallback: true,\n },\n async (ctx) => {\n const packages = await this._getPackages(componentUrl);\n ctx.cacheable = isNonEmptyObject(packages);\n return packages;\n },\n );\n }\n\n /**\n * Gets releases for a specific package from APK repositories\n */\n async getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore if -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n logger.debug(`Getting APK releases for ${packageName} from ${registryUrl}`);\n\n const componentUrls = constructComponentUrls(registryUrl);\n let result: ReleaseResult | null = null;\n // A package can be served by more than one component, so the same version\n // must not be reported twice\n const seenVersions = new Set<string>();\n\n for (const componentUrl of componentUrls) {\n try {\n const packages = await this.getPackages(componentUrl);\n const matchingPackages = packages[packageName];\n\n if (!matchingPackages) {\n logger.debug(\n { packageName, componentUrl },\n 'No matching packages found',\n );\n continue;\n }\n\n const releases: Release[] = [];\n for (const pkg of matchingPackages) {\n if (seenVersions.has(pkg.version)) {\n continue;\n }\n seenVersions.add(pkg.version);\n releases.push({\n version: pkg.version,\n releaseTimestamp: pkg.buildDate\n ? asTimestamp(pkg.buildDate * 1000)\n : undefined,\n });\n }\n\n logger.trace(\n {\n packageName,\n componentUrl,\n releaseCount: releases.length,\n releases,\n },\n 'Found APK releases',\n );\n\n result ??= { releases: [], registryUrl };\n result.homepage ??= matchingPackages.find((pkg) => pkg.url)?.url;\n result.releases.push(...releases);\n } catch (err) {\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug(\n { packageName, componentUrl, err },\n 'Skipping APK component due to an error',\n );\n }\n }\n\n return result;\n }\n}\n"],"mappings":"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"}
@@ -35,7 +35,7 @@ var AzureBicepResourceDatasource = class AzureBicepResourceDatasource extends Da
35
35
  return this.cached({
36
36
  key: buildCacheKey("getReleases", config.packageName),
37
37
  fallback: true,
38
- cacheable: true
38
+ public: true
39
39
  }, () => this.fetchReleases(config));
40
40
  }
41
41
  async fetchResourceVersionIndex() {
@@ -46,7 +46,7 @@ var AzureBicepResourceDatasource = class AzureBicepResourceDatasource extends Da
46
46
  return this.cached({
47
47
  key: "getResourceVersionIndex",
48
48
  ttlMinutes: 1440,
49
- cacheable: true
49
+ public: true
50
50
  }, () => this.fetchResourceVersionIndex());
51
51
  }
52
52
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["azureRestApiVersioningApi.id"],"sources":["../../../../lib/modules/datasource/azure-bicep-resource/index.ts"],"sourcesContent":["import { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport * as azureRestApiVersioningApi from '../../versioning/azure-rest-api/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { BicepResourceVersionIndex } from './schema.ts';\n\nconst BICEP_TYPES_INDEX_URL =\n 'https://raw.githubusercontent.com/Azure/bicep-types-az/main/generated/index.json';\n\nexport class AzureBicepResourceDatasource extends Datasource {\n static readonly id = 'azure-bicep-resource';\n\n override readonly defaultConfig = {\n commitMessageTopic: 'resource {{depName}}',\n commitMessageExtra: 'to {{{newVersion}}}',\n prBodyColumns: ['Resource', 'Change'],\n prBodyDefinitions: {\n Resource: '{{{depNameLinked}}}',\n },\n };\n\n override readonly defaultVersioning = azureRestApiVersioningApi.id;\n\n constructor() {\n super(AzureBicepResourceDatasource.id);\n }\n\n private getChangelogUrl(packageName: string): string {\n const firstSlashIndex = packageName.indexOf('/');\n const namespaceProvider = packageName.slice(0, firstSlashIndex);\n const type = packageName.slice(firstSlashIndex + 1);\n return `https://learn.microsoft.com/en-us/azure/templates/${namespaceProvider}/change-log/${type}`;\n }\n\n private async fetchReleases(\n getReleasesConfig: GetReleasesConfig,\n ): Promise<ReleaseResult | null> {\n const resourceVersionIndex = await this.getResourceVersionIndex();\n const packageName = getReleasesConfig.packageName.toLowerCase();\n const versions = resourceVersionIndex[packageName];\n if (!versions?.length) {\n return null;\n }\n\n const changelogUrl = this.getChangelogUrl(packageName);\n const releases = versions.map((version) => ({\n version,\n changelogUrl: `${changelogUrl}#${version}`,\n }));\n return { releases };\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return this.cached(\n {\n key: buildCacheKey('getReleases', config.packageName),\n fallback: true,\n cacheable: true,\n },\n () => this.fetchReleases(config),\n );\n }\n\n private async fetchResourceVersionIndex(): Promise<BicepResourceVersionIndex> {\n const { body } = await this.http.getJson(\n BICEP_TYPES_INDEX_URL,\n BicepResourceVersionIndex,\n );\n return body;\n }\n\n getResourceVersionIndex(): Promise<BicepResourceVersionIndex> {\n return this.cached(\n {\n key: 'getResourceVersionIndex',\n ttlMinutes: 24 * 60,\n cacheable: true,\n },\n () => this.fetchResourceVersionIndex(),\n );\n }\n}\n"],"mappings":";;;;;AAMA,MAAM,wBACJ;AAEF,IAAa,+BAAb,MAAa,qCAAqC,WAAW;CAC3D,OAAgB,KAAK;CAErB,gBAAkC;EAChC,oBAAoB;EACpB,oBAAoB;EACpB,eAAe,CAAC,YAAY,QAAQ;EACpC,mBAAmB,EACjB,UAAU,sBACZ;CACF;CAEA,oBAAsCA;CAEtC,cAAc;EACZ,MAAM,6BAA6B,EAAE;CACvC;CAEA,gBAAwB,aAA6B;EACnD,MAAM,kBAAkB,YAAY,QAAQ,GAAG;EAG/C,OAAO,qDAFmB,YAAY,MAAM,GAAG,eAE6B,EAAE,cADjE,YAAY,MAAM,kBAAkB,CAC8C;CACjG;CAEA,MAAc,cACZ,mBAC+B;EAC/B,MAAM,uBAAuB,MAAM,KAAK,wBAAwB;EAChE,MAAM,cAAc,kBAAkB,YAAY,YAAY;EAC9D,MAAM,WAAW,qBAAqB;EACtC,IAAI,CAAC,UAAU,QACb,OAAO;EAGT,MAAM,eAAe,KAAK,gBAAgB,WAAW;EAKrD,OAAO,EAAE,UAJQ,SAAS,KAAK,aAAa;GAC1C;GACA,cAAc,GAAG,aAAa,GAAG;EACnC,EACgB,EAAE;CACpB;CAEA,YAAY,QAA0D;EACpE,OAAO,KAAK,OACV;GACE,KAAK,cAAc,eAAe,OAAO,WAAW;GACpD,UAAU;GACV,WAAW;EACb,SACM,KAAK,cAAc,MAAM,CACjC;CACF;CAEA,MAAc,4BAAgE;EAC5E,MAAM,EAAE,SAAS,MAAM,KAAK,KAAK,QAC/B,uBACA,yBACF;EACA,OAAO;CACT;CAEA,0BAA8D;EAC5D,OAAO,KAAK,OACV;GACE,KAAK;GACL,YAAY;GACZ,WAAW;EACb,SACM,KAAK,0BAA0B,CACvC;CACF;AACF"}
1
+ {"version":3,"file":"index.js","names":["azureRestApiVersioningApi.id"],"sources":["../../../../lib/modules/datasource/azure-bicep-resource/index.ts"],"sourcesContent":["import { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport * as azureRestApiVersioningApi from '../../versioning/azure-rest-api/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { BicepResourceVersionIndex } from './schema.ts';\n\nconst BICEP_TYPES_INDEX_URL =\n 'https://raw.githubusercontent.com/Azure/bicep-types-az/main/generated/index.json';\n\nexport class AzureBicepResourceDatasource extends Datasource {\n static readonly id = 'azure-bicep-resource';\n\n override readonly defaultConfig = {\n commitMessageTopic: 'resource {{depName}}',\n commitMessageExtra: 'to {{{newVersion}}}',\n prBodyColumns: ['Resource', 'Change'],\n prBodyDefinitions: {\n Resource: '{{{depNameLinked}}}',\n },\n };\n\n override readonly defaultVersioning = azureRestApiVersioningApi.id;\n\n constructor() {\n super(AzureBicepResourceDatasource.id);\n }\n\n private getChangelogUrl(packageName: string): string {\n const firstSlashIndex = packageName.indexOf('/');\n const namespaceProvider = packageName.slice(0, firstSlashIndex);\n const type = packageName.slice(firstSlashIndex + 1);\n return `https://learn.microsoft.com/en-us/azure/templates/${namespaceProvider}/change-log/${type}`;\n }\n\n private async fetchReleases(\n getReleasesConfig: GetReleasesConfig,\n ): Promise<ReleaseResult | null> {\n const resourceVersionIndex = await this.getResourceVersionIndex();\n const packageName = getReleasesConfig.packageName.toLowerCase();\n const versions = resourceVersionIndex[packageName];\n if (!versions?.length) {\n return null;\n }\n\n const changelogUrl = this.getChangelogUrl(packageName);\n const releases = versions.map((version) => ({\n version,\n changelogUrl: `${changelogUrl}#${version}`,\n }));\n return { releases };\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return this.cached(\n {\n key: buildCacheKey('getReleases', config.packageName),\n fallback: true,\n public: true,\n },\n () => this.fetchReleases(config),\n );\n }\n\n private async fetchResourceVersionIndex(): Promise<BicepResourceVersionIndex> {\n const { body } = await this.http.getJson(\n BICEP_TYPES_INDEX_URL,\n BicepResourceVersionIndex,\n );\n return body;\n }\n\n getResourceVersionIndex(): Promise<BicepResourceVersionIndex> {\n return this.cached(\n {\n key: 'getResourceVersionIndex',\n ttlMinutes: 24 * 60,\n public: true,\n },\n () => this.fetchResourceVersionIndex(),\n );\n }\n}\n"],"mappings":";;;;;AAMA,MAAM,wBACJ;AAEF,IAAa,+BAAb,MAAa,qCAAqC,WAAW;CAC3D,OAAgB,KAAK;CAErB,gBAAkC;EAChC,oBAAoB;EACpB,oBAAoB;EACpB,eAAe,CAAC,YAAY,QAAQ;EACpC,mBAAmB,EACjB,UAAU,sBACZ;CACF;CAEA,oBAAsCA;CAEtC,cAAc;EACZ,MAAM,6BAA6B,EAAE;CACvC;CAEA,gBAAwB,aAA6B;EACnD,MAAM,kBAAkB,YAAY,QAAQ,GAAG;EAG/C,OAAO,qDAFmB,YAAY,MAAM,GAAG,eAE6B,EAAE,cADjE,YAAY,MAAM,kBAAkB,CAC8C;CACjG;CAEA,MAAc,cACZ,mBAC+B;EAC/B,MAAM,uBAAuB,MAAM,KAAK,wBAAwB;EAChE,MAAM,cAAc,kBAAkB,YAAY,YAAY;EAC9D,MAAM,WAAW,qBAAqB;EACtC,IAAI,CAAC,UAAU,QACb,OAAO;EAGT,MAAM,eAAe,KAAK,gBAAgB,WAAW;EAKrD,OAAO,EAAE,UAJQ,SAAS,KAAK,aAAa;GAC1C;GACA,cAAc,GAAG,aAAa,GAAG;EACnC,EACgB,EAAE;CACpB;CAEA,YAAY,QAA0D;EACpE,OAAO,KAAK,OACV;GACE,KAAK,cAAc,eAAe,OAAO,WAAW;GACpD,UAAU;GACV,QAAQ;EACV,SACM,KAAK,cAAc,MAAM,CACjC;CACF;CAEA,MAAc,4BAAgE;EAC5E,MAAM,EAAE,SAAS,MAAM,KAAK,KAAK,QAC/B,uBACA,yBACF;EACA,OAAO;CACT;CAEA,0BAA8D;EAC5D,OAAO,KAAK,OACV;GACE,KAAK;GACL,YAAY;GACZ,QAAQ;EACV,SACM,KAAK,0BAA0B,CACvC;CACF;AACF"}
@@ -35,7 +35,7 @@ var BuildpacksRegistryDatasource = class BuildpacksRegistryDatasource extends Da
35
35
  return withCache({
36
36
  namespace: `datasource-${BuildpacksRegistryDatasource.id}`,
37
37
  key: buildCacheKey(config.registryUrl, config.packageName),
38
- cacheable: true,
38
+ public: true,
39
39
  fallback: true
40
40
  }, () => this._getReleases(config));
41
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../lib/modules/datasource/buildpacks-registry/index.ts"],"sourcesContent":["import urlJoin from 'url-join';\nimport { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, Release, ReleaseResult } from '../types.ts';\nimport { BuildpacksRegistryResponse } from './schema.ts';\n\nexport class BuildpacksRegistryDatasource extends Datasource {\n static readonly id = 'buildpacks-registry';\n\n constructor() {\n super(BuildpacksRegistryDatasource.id);\n }\n\n override supportsCustomRegistry(_packageName: string): boolean {\n return false;\n }\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return ['https://registry.buildpacks.io'];\n }\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `published_at` field in the results.';\n override readonly sourceUrlSupport = 'release';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `source_code_url` field of the release object in the results.';\n\n private async _getReleases(\n config: GetReleasesConfig,\n ): Promise<ReleaseResult | null> {\n const { packageName, registryUrl } = config;\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n const url = urlJoin(registryUrl, 'api', 'v1', 'buildpacks', packageName);\n\n const body = await this.fetchJsonOrNull(url, BuildpacksRegistryResponse);\n if (!body) {\n return null;\n }\n\n const { versions, latest } = body;\n const releases: Release[] = versions;\n\n const res: ReleaseResult = { releases };\n\n if (latest?.homepage) {\n res.homepage = latest.homepage;\n }\n\n return res;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${BuildpacksRegistryDatasource.id}`,\n key: buildCacheKey(config.registryUrl, config.packageName),\n cacheable: true,\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n}\n"],"mappings":";;;;;;AAOA,IAAa,+BAAb,MAAa,qCAAqC,WAAW;CAC3D,OAAgB,KAAK;CAErB,cAAc;EACZ,MAAM,6BAA6B,EAAE;CACvC;CAEA,uBAAgC,cAA+B;EAC7D,OAAO;CACT;CAEA,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,gCAAgC;CAC1C;CAEA,0BAA4C;CAC5C,uBACE;CACF,mBAAqC;CACrC,gBACE;CAEF,MAAc,aACZ,QAC+B;EAC/B,MAAM,EAAE,aAAa,gBAAgB;;EAErC,IAAI,CAAC,aACH,OAAO;EAGT,MAAM,MAAM,QAAQ,aAAa,OAAO,MAAM,cAAc,WAAW;EAEvE,MAAM,OAAO,MAAM,KAAK,gBAAgB,KAAK,0BAA0B;EACvE,IAAI,CAAC,MACH,OAAO;EAGT,MAAM,EAAE,UAAU,WAAW;EAG7B,MAAM,MAAqB,EAAE,UAAA,SAAS;EAEtC,IAAI,QAAQ,UACV,IAAI,WAAW,OAAO;EAGxB,OAAO;CACT;CAEA,YAAY,QAA0D;EACpE,OAAO,UACL;GACE,WAAW,cAAc,6BAA6B;GACtD,KAAK,cAAc,OAAO,aAAa,OAAO,WAAW;GACzD,WAAW;GACX,UAAU;EACZ,SACM,KAAK,aAAa,MAAM,CAChC;CACF;AACF"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../lib/modules/datasource/buildpacks-registry/index.ts"],"sourcesContent":["import urlJoin from 'url-join';\nimport { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, Release, ReleaseResult } from '../types.ts';\nimport { BuildpacksRegistryResponse } from './schema.ts';\n\nexport class BuildpacksRegistryDatasource extends Datasource {\n static readonly id = 'buildpacks-registry';\n\n constructor() {\n super(BuildpacksRegistryDatasource.id);\n }\n\n override supportsCustomRegistry(_packageName: string): boolean {\n return false;\n }\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return ['https://registry.buildpacks.io'];\n }\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `published_at` field in the results.';\n override readonly sourceUrlSupport = 'release';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `source_code_url` field of the release object in the results.';\n\n private async _getReleases(\n config: GetReleasesConfig,\n ): Promise<ReleaseResult | null> {\n const { packageName, registryUrl } = config;\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n const url = urlJoin(registryUrl, 'api', 'v1', 'buildpacks', packageName);\n\n const body = await this.fetchJsonOrNull(url, BuildpacksRegistryResponse);\n if (!body) {\n return null;\n }\n\n const { versions, latest } = body;\n const releases: Release[] = versions;\n\n const res: ReleaseResult = { releases };\n\n if (latest?.homepage) {\n res.homepage = latest.homepage;\n }\n\n return res;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${BuildpacksRegistryDatasource.id}`,\n key: buildCacheKey(config.registryUrl, config.packageName),\n public: true,\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n}\n"],"mappings":";;;;;;AAOA,IAAa,+BAAb,MAAa,qCAAqC,WAAW;CAC3D,OAAgB,KAAK;CAErB,cAAc;EACZ,MAAM,6BAA6B,EAAE;CACvC;CAEA,uBAAgC,cAA+B;EAC7D,OAAO;CACT;CAEA,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,gCAAgC;CAC1C;CAEA,0BAA4C;CAC5C,uBACE;CACF,mBAAqC;CACrC,gBACE;CAEF,MAAc,aACZ,QAC+B;EAC/B,MAAM,EAAE,aAAa,gBAAgB;;EAErC,IAAI,CAAC,aACH,OAAO;EAGT,MAAM,MAAM,QAAQ,aAAa,OAAO,MAAM,cAAc,WAAW;EAEvE,MAAM,OAAO,MAAM,KAAK,gBAAgB,KAAK,0BAA0B;EACvE,IAAI,CAAC,MACH,OAAO;EAGT,MAAM,EAAE,UAAU,WAAW;EAG7B,MAAM,MAAqB,EAAE,UAAA,SAAS;EAEtC,IAAI,QAAQ,UACV,IAAI,WAAW,OAAO;EAGxB,OAAO;CACT;CAEA,YAAY,QAA0D;EACpE,OAAO,UACL;GACE,WAAW,cAAc,6BAA6B;GACtD,KAAK,cAAc,OAAO,aAAa,OAAO,WAAW;GACzD,QAAQ;GACR,UAAU;EACZ,SACM,KAAK,aAAa,MAAM,CAChC;CACF;AACF"}
@@ -37,7 +37,7 @@ var CdnjsDatasource = class CdnjsDatasource extends Datasource {
37
37
  return withCache({
38
38
  namespace: `datasource-${CdnjsDatasource.id}`,
39
39
  key: buildCacheKey("getReleases", library),
40
- cacheable: true,
40
+ public: true,
41
41
  fallback: true
42
42
  }, () => this._getReleases(config));
43
43
  }
@@ -54,7 +54,7 @@ var CdnjsDatasource = class CdnjsDatasource extends Datasource {
54
54
  return withCache({
55
55
  namespace: `datasource-${CdnjsDatasource.id}`,
56
56
  key: buildCacheKey("getDigest", config.registryUrl, config.packageName, newValue),
57
- cacheable: true,
57
+ public: true,
58
58
  fallback: true
59
59
  }, () => this._getDigest(config, newValue));
60
60
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../lib/modules/datasource/cdnjs/index.ts"],"sourcesContent":["import { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { memCacheProvider } from '../../../util/http/cache/memory-http-cache-provider.ts';\nimport type { HttpError } from '../../../util/http/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type {\n DigestConfig,\n GetReleasesConfig,\n Release,\n ReleaseResult,\n} from '../types.ts';\nimport { CdnjsAPISriResponse, CdnjsAPIVersionResponse } from './schema.ts';\n\nexport class CdnjsDatasource extends Datasource {\n static readonly id = 'cdnjs';\n\n constructor() {\n super(CdnjsDatasource.id);\n }\n\n override supportsCustomRegistry(_packageName: string): boolean {\n return false;\n }\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return ['https://api.cdnjs.com/'];\n }\n\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `repository` field in the results.';\n\n private async _getReleases(\n config: GetReleasesConfig,\n ): Promise<ReleaseResult | null> {\n const { packageName, registryUrl } = config;\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n const [library] = packageName.split('/');\n\n const url = `${registryUrl}libraries/${library}?fields=homepage,repository,versions`;\n\n const body = await this.fetchJsonOrNull(url, CdnjsAPIVersionResponse, {\n cacheProvider: memCacheProvider,\n });\n if (!body) {\n return null;\n }\n\n const { versions, homepage, repository } = body;\n const releases: Release[] = versions;\n\n const res: ReleaseResult = { releases };\n\n if (homepage) {\n res.homepage = homepage;\n }\n\n if (repository) {\n res.sourceUrl = repository;\n }\n\n return res;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n const library = config.packageName.split('/')[0];\n return withCache(\n {\n namespace: `datasource-${CdnjsDatasource.id}`,\n key: buildCacheKey('getReleases', library),\n cacheable: true,\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n\n private async _getDigest(\n config: DigestConfig,\n newValue: string,\n ): Promise<string | null> {\n const { packageName, registryUrl } = config;\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n const [library] = packageName.split('/');\n const assetName = packageName.replace(`${library}/`, '');\n\n const url = `${registryUrl}libraries/${library}/${newValue}?fields=sri`;\n\n const body = await this.fetchJsonOrNull(url, CdnjsAPISriResponse);\n\n return body?.sri?.[assetName] ?? null;\n }\n\n override getDigest(\n config: DigestConfig,\n newValue: string,\n ): Promise<string | null> {\n return withCache(\n {\n namespace: `datasource-${CdnjsDatasource.id}`,\n key: buildCacheKey(\n 'getDigest',\n config.registryUrl,\n config.packageName,\n newValue,\n ),\n cacheable: true,\n fallback: true,\n },\n () => this._getDigest(config, newValue),\n );\n }\n\n override handleHttpErrors(err: HttpError): void {\n if (err.response?.statusCode !== 404) {\n throw new ExternalHostError(err);\n }\n }\n}\n"],"mappings":";;;;;;;AAcA,IAAa,kBAAb,MAAa,wBAAwB,WAAW;CAC9C,OAAgB,KAAK;CAErB,cAAc;EACZ,MAAM,gBAAgB,EAAE;CAC1B;CAEA,uBAAgC,cAA+B;EAC7D,OAAO;CACT;CAEA,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,wBAAwB;CAClC;CAEA,mBAAqC;CACrC,gBACE;CAEF,MAAc,aACZ,QAC+B;EAC/B,MAAM,EAAE,aAAa,gBAAgB;;EAErC,IAAI,CAAC,aACH,OAAO;EAGT,MAAM,CAAC,WAAW,YAAY,MAAM,GAAG;EAEvC,MAAM,MAAM,GAAG,YAAY,YAAY,QAAQ;EAE/C,MAAM,OAAO,MAAM,KAAK,gBAAgB,KAAK,yBAAyB,EACpE,eAAe,iBACjB,CAAC;EACD,IAAI,CAAC,MACH,OAAO;EAGT,MAAM,EAAE,UAAU,UAAU,eAAe;EAG3C,MAAM,MAAqB,EAAE,UAAA,SAAS;EAEtC,IAAI,UACF,IAAI,WAAW;EAGjB,IAAI,YACF,IAAI,YAAY;EAGlB,OAAO;CACT;CAEA,YAAY,QAA0D;EACpE,MAAM,UAAU,OAAO,YAAY,MAAM,GAAG,CAAC,CAAC;EAC9C,OAAO,UACL;GACE,WAAW,cAAc,gBAAgB;GACzC,KAAK,cAAc,eAAe,OAAO;GACzC,WAAW;GACX,UAAU;EACZ,SACM,KAAK,aAAa,MAAM,CAChC;CACF;CAEA,MAAc,WACZ,QACA,UACwB;EACxB,MAAM,EAAE,aAAa,gBAAgB;;EAErC,IAAI,CAAC,aACH,OAAO;EAGT,MAAM,CAAC,WAAW,YAAY,MAAM,GAAG;EACvC,MAAM,YAAY,YAAY,QAAQ,GAAG,QAAQ,IAAI,EAAE;EAEvD,MAAM,MAAM,GAAG,YAAY,YAAY,QAAQ,GAAG,SAAS;EAI3D,QAAO,MAFY,KAAK,gBAAgB,KAAK,mBAAmB,EAAA,EAEnD,MAAM,cAAc;CACnC;CAEA,UACE,QACA,UACwB;EACxB,OAAO,UACL;GACE,WAAW,cAAc,gBAAgB;GACzC,KAAK,cACH,aACA,OAAO,aACP,OAAO,aACP,QACF;GACA,WAAW;GACX,UAAU;EACZ,SACM,KAAK,WAAW,QAAQ,QAAQ,CACxC;CACF;CAEA,iBAA0B,KAAsB;EAC9C,IAAI,IAAI,UAAU,eAAe,KAC/B,MAAM,IAAI,kBAAkB,GAAG;CAEnC;AACF"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../lib/modules/datasource/cdnjs/index.ts"],"sourcesContent":["import { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { memCacheProvider } from '../../../util/http/cache/memory-http-cache-provider.ts';\nimport type { HttpError } from '../../../util/http/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type {\n DigestConfig,\n GetReleasesConfig,\n Release,\n ReleaseResult,\n} from '../types.ts';\nimport { CdnjsAPISriResponse, CdnjsAPIVersionResponse } from './schema.ts';\n\nexport class CdnjsDatasource extends Datasource {\n static readonly id = 'cdnjs';\n\n constructor() {\n super(CdnjsDatasource.id);\n }\n\n override supportsCustomRegistry(_packageName: string): boolean {\n return false;\n }\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return ['https://api.cdnjs.com/'];\n }\n\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `repository` field in the results.';\n\n private async _getReleases(\n config: GetReleasesConfig,\n ): Promise<ReleaseResult | null> {\n const { packageName, registryUrl } = config;\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n const [library] = packageName.split('/');\n\n const url = `${registryUrl}libraries/${library}?fields=homepage,repository,versions`;\n\n const body = await this.fetchJsonOrNull(url, CdnjsAPIVersionResponse, {\n cacheProvider: memCacheProvider,\n });\n if (!body) {\n return null;\n }\n\n const { versions, homepage, repository } = body;\n const releases: Release[] = versions;\n\n const res: ReleaseResult = { releases };\n\n if (homepage) {\n res.homepage = homepage;\n }\n\n if (repository) {\n res.sourceUrl = repository;\n }\n\n return res;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n const library = config.packageName.split('/')[0];\n return withCache(\n {\n namespace: `datasource-${CdnjsDatasource.id}`,\n key: buildCacheKey('getReleases', library),\n public: true,\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n\n private async _getDigest(\n config: DigestConfig,\n newValue: string,\n ): Promise<string | null> {\n const { packageName, registryUrl } = config;\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n const [library] = packageName.split('/');\n const assetName = packageName.replace(`${library}/`, '');\n\n const url = `${registryUrl}libraries/${library}/${newValue}?fields=sri`;\n\n const body = await this.fetchJsonOrNull(url, CdnjsAPISriResponse);\n\n return body?.sri?.[assetName] ?? null;\n }\n\n override getDigest(\n config: DigestConfig,\n newValue: string,\n ): Promise<string | null> {\n return withCache(\n {\n namespace: `datasource-${CdnjsDatasource.id}`,\n key: buildCacheKey(\n 'getDigest',\n config.registryUrl,\n config.packageName,\n newValue,\n ),\n public: true,\n fallback: true,\n },\n () => this._getDigest(config, newValue),\n );\n }\n\n override handleHttpErrors(err: HttpError): void {\n if (err.response?.statusCode !== 404) {\n throw new ExternalHostError(err);\n }\n }\n}\n"],"mappings":";;;;;;;AAcA,IAAa,kBAAb,MAAa,wBAAwB,WAAW;CAC9C,OAAgB,KAAK;CAErB,cAAc;EACZ,MAAM,gBAAgB,EAAE;CAC1B;CAEA,uBAAgC,cAA+B;EAC7D,OAAO;CACT;CAEA,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,wBAAwB;CAClC;CAEA,mBAAqC;CACrC,gBACE;CAEF,MAAc,aACZ,QAC+B;EAC/B,MAAM,EAAE,aAAa,gBAAgB;;EAErC,IAAI,CAAC,aACH,OAAO;EAGT,MAAM,CAAC,WAAW,YAAY,MAAM,GAAG;EAEvC,MAAM,MAAM,GAAG,YAAY,YAAY,QAAQ;EAE/C,MAAM,OAAO,MAAM,KAAK,gBAAgB,KAAK,yBAAyB,EACpE,eAAe,iBACjB,CAAC;EACD,IAAI,CAAC,MACH,OAAO;EAGT,MAAM,EAAE,UAAU,UAAU,eAAe;EAG3C,MAAM,MAAqB,EAAE,UAAA,SAAS;EAEtC,IAAI,UACF,IAAI,WAAW;EAGjB,IAAI,YACF,IAAI,YAAY;EAGlB,OAAO;CACT;CAEA,YAAY,QAA0D;EACpE,MAAM,UAAU,OAAO,YAAY,MAAM,GAAG,CAAC,CAAC;EAC9C,OAAO,UACL;GACE,WAAW,cAAc,gBAAgB;GACzC,KAAK,cAAc,eAAe,OAAO;GACzC,QAAQ;GACR,UAAU;EACZ,SACM,KAAK,aAAa,MAAM,CAChC;CACF;CAEA,MAAc,WACZ,QACA,UACwB;EACxB,MAAM,EAAE,aAAa,gBAAgB;;EAErC,IAAI,CAAC,aACH,OAAO;EAGT,MAAM,CAAC,WAAW,YAAY,MAAM,GAAG;EACvC,MAAM,YAAY,YAAY,QAAQ,GAAG,QAAQ,IAAI,EAAE;EAEvD,MAAM,MAAM,GAAG,YAAY,YAAY,QAAQ,GAAG,SAAS;EAI3D,QAAO,MAFY,KAAK,gBAAgB,KAAK,mBAAmB,EAAA,EAEnD,MAAM,cAAc;CACnC;CAEA,UACE,QACA,UACwB;EACxB,OAAO,UACL;GACE,WAAW,cAAc,gBAAgB;GACzC,KAAK,cACH,aACA,OAAO,aACP,OAAO,aACP,QACF;GACA,QAAQ;GACR,UAAU;EACZ,SACM,KAAK,WAAW,QAAQ,QAAQ,CACxC;CACF;CAEA,iBAA0B,KAAsB;EAC9C,IAAI,IAAI,UAAU,eAAe,KAC/B,MAAM,IAAI,kBAAkB,GAAG;CAEnC;AACF"}
@@ -70,7 +70,7 @@ var CpanDatasource = class CpanDatasource extends Datasource {
70
70
  return withCache({
71
71
  namespace: `datasource-${CpanDatasource.id}`,
72
72
  key: config.packageName,
73
- cacheable: true,
73
+ public: true,
74
74
  fallback: true
75
75
  }, () => this._getReleases(config));
76
76
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["perlVersioning.id"],"sources":["../../../../lib/modules/datasource/cpan/index.ts"],"sourcesContent":["import { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { joinUrlParts } from '../../../util/url.ts';\nimport * as perlVersioning from '../../versioning/perl/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { MetaCpanApiFileSearchResponse } from './schema.ts';\nimport type { CpanRelease } from './types.ts';\n\nexport class CpanDatasource extends Datasource {\n static readonly id = 'cpan';\n\n constructor() {\n super(CpanDatasource.id);\n }\n\n override supportsCustomRegistry(_packageName: string): boolean {\n return false;\n }\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return ['https://fastapi.metacpan.org/'];\n }\n\n override readonly defaultVersioning = perlVersioning.id;\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `date` field in the results.';\n\n private async _getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n let result: ReleaseResult | null = null;\n const searchUrl = joinUrlParts(registryUrl, 'v1/file/_search');\n\n let releases: CpanRelease[] | null = null;\n try {\n const body = {\n query: {\n bool: {\n filter: [\n { term: { 'module.name': packageName } },\n { term: { 'module.authorized': true } },\n { exists: { field: 'module.associated_pod' } },\n ],\n },\n },\n _source: [\n 'module.name',\n 'module.version',\n 'distribution',\n 'date',\n 'deprecated',\n 'maturity',\n 'status',\n ],\n sort: [{ date: 'desc' }],\n };\n\n releases = (\n await this.http.postJson(\n searchUrl,\n { body },\n MetaCpanApiFileSearchResponse,\n )\n ).body;\n } catch (err) {\n this.handleGenericErrors(err);\n }\n\n let latestDistribution: string | null = null;\n let latestVersion: string | null = null;\n // `releases` is only still null if the request above threw, and\n // `handleGenericErrors()` always rethrows - the `releases.length` below\n // would dereference null if the else were ever taken\n // v8 ignore else -- unreachable\n if (releases) {\n for (const release of releases) {\n latestDistribution ??= release.distribution;\n if (!latestVersion && release.isLatest) {\n latestVersion = release.version;\n }\n }\n }\n if (releases.length > 0 && latestDistribution) {\n result = {\n releases,\n changelogUrl: `https://metacpan.org/dist/${latestDistribution}/changes`,\n homepage: `https://metacpan.org/pod/${packageName}`,\n };\n\n if (latestVersion) {\n result.tags ??= {};\n result.tags.latest = latestVersion;\n }\n }\n\n return result;\n }\n\n override getReleases(\n config: GetReleasesConfig,\n ): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${CpanDatasource.id}`,\n key: config.packageName,\n cacheable: true,\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n}\n"],"mappings":";;;;;;AAQA,IAAa,iBAAb,MAAa,uBAAuB,WAAW;CAC7C,OAAgB,KAAK;CAErB,cAAc;EACZ,MAAM,eAAe,EAAE;CACzB;CAEA,uBAAgC,cAA+B;EAC7D,OAAO;CACT;CAEA,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,+BAA+B;CACzC;CAEA,oBAAsCA;CAEtC,0BAA4C;CAC5C,uBACE;CAEF,MAAc,aAAa,EACzB,aACA,eACmD;;EAEnD,IAAI,CAAC,aACH,OAAO;EAGT,IAAI,SAA+B;EACnC,MAAM,YAAY,aAAa,aAAa,iBAAiB;EAE7D,IAAI,WAAiC;EACrC,IAAI;GACF,MAAM,OAAO;IACX,OAAO,EACL,MAAM,EACJ,QAAQ;KACN,EAAE,MAAM,EAAE,eAAe,YAAY,EAAE;KACvC,EAAE,MAAM,EAAE,qBAAqB,KAAK,EAAE;KACtC,EAAE,QAAQ,EAAE,OAAO,wBAAwB,EAAE;IAC/C,EACF,EACF;IACA,SAAS;KACP;KACA;KACA;KACA;KACA;KACA;KACA;IACF;IACA,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC;GACzB;GAEA,YACE,MAAM,KAAK,KAAK,SACd,WACA,EAAE,KAAK,GACP,6BACF,EAAA,CACA;EACJ,SAAS,KAAK;GACZ,KAAK,oBAAoB,GAAG;EAC9B;EAEA,IAAI,qBAAoC;EACxC,IAAI,gBAA+B;;EAKnC,IAAI,UACF,KAAK,MAAM,WAAW,UAAU;GAC9B,uBAAuB,QAAQ;GAC/B,IAAI,CAAC,iBAAiB,QAAQ,UAC5B,gBAAgB,QAAQ;EAE5B;EAEF,IAAI,SAAS,SAAS,KAAK,oBAAoB;GAC7C,SAAS;IACP;IACA,cAAc,6BAA6B,mBAAmB;IAC9D,UAAU,4BAA4B;GACxC;GAEA,IAAI,eAAe;IACjB,OAAO,SAAS,CAAC;IACjB,OAAO,KAAK,SAAS;GACvB;EACF;EAEA,OAAO;CACT;CAEA,YACE,QAC+B;EAC/B,OAAO,UACL;GACE,WAAW,cAAc,eAAe;GACxC,KAAK,OAAO;GACZ,WAAW;GACX,UAAU;EACZ,SACM,KAAK,aAAa,MAAM,CAChC;CACF;AACF"}
1
+ {"version":3,"file":"index.js","names":["perlVersioning.id"],"sources":["../../../../lib/modules/datasource/cpan/index.ts"],"sourcesContent":["import { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { joinUrlParts } from '../../../util/url.ts';\nimport * as perlVersioning from '../../versioning/perl/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { MetaCpanApiFileSearchResponse } from './schema.ts';\nimport type { CpanRelease } from './types.ts';\n\nexport class CpanDatasource extends Datasource {\n static readonly id = 'cpan';\n\n constructor() {\n super(CpanDatasource.id);\n }\n\n override supportsCustomRegistry(_packageName: string): boolean {\n return false;\n }\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return ['https://fastapi.metacpan.org/'];\n }\n\n override readonly defaultVersioning = perlVersioning.id;\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `date` field in the results.';\n\n private async _getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n let result: ReleaseResult | null = null;\n const searchUrl = joinUrlParts(registryUrl, 'v1/file/_search');\n\n let releases: CpanRelease[] | null = null;\n try {\n const body = {\n query: {\n bool: {\n filter: [\n { term: { 'module.name': packageName } },\n { term: { 'module.authorized': true } },\n { exists: { field: 'module.associated_pod' } },\n ],\n },\n },\n _source: [\n 'module.name',\n 'module.version',\n 'distribution',\n 'date',\n 'deprecated',\n 'maturity',\n 'status',\n ],\n sort: [{ date: 'desc' }],\n };\n\n releases = (\n await this.http.postJson(\n searchUrl,\n { body },\n MetaCpanApiFileSearchResponse,\n )\n ).body;\n } catch (err) {\n this.handleGenericErrors(err);\n }\n\n let latestDistribution: string | null = null;\n let latestVersion: string | null = null;\n // `releases` is only still null if the request above threw, and\n // `handleGenericErrors()` always rethrows - the `releases.length` below\n // would dereference null if the else were ever taken\n // v8 ignore else -- unreachable\n if (releases) {\n for (const release of releases) {\n latestDistribution ??= release.distribution;\n if (!latestVersion && release.isLatest) {\n latestVersion = release.version;\n }\n }\n }\n if (releases.length > 0 && latestDistribution) {\n result = {\n releases,\n changelogUrl: `https://metacpan.org/dist/${latestDistribution}/changes`,\n homepage: `https://metacpan.org/pod/${packageName}`,\n };\n\n if (latestVersion) {\n result.tags ??= {};\n result.tags.latest = latestVersion;\n }\n }\n\n return result;\n }\n\n override getReleases(\n config: GetReleasesConfig,\n ): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${CpanDatasource.id}`,\n key: config.packageName,\n public: true,\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n}\n"],"mappings":";;;;;;AAQA,IAAa,iBAAb,MAAa,uBAAuB,WAAW;CAC7C,OAAgB,KAAK;CAErB,cAAc;EACZ,MAAM,eAAe,EAAE;CACzB;CAEA,uBAAgC,cAA+B;EAC7D,OAAO;CACT;CAEA,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,+BAA+B;CACzC;CAEA,oBAAsCA;CAEtC,0BAA4C;CAC5C,uBACE;CAEF,MAAc,aAAa,EACzB,aACA,eACmD;;EAEnD,IAAI,CAAC,aACH,OAAO;EAGT,IAAI,SAA+B;EACnC,MAAM,YAAY,aAAa,aAAa,iBAAiB;EAE7D,IAAI,WAAiC;EACrC,IAAI;GACF,MAAM,OAAO;IACX,OAAO,EACL,MAAM,EACJ,QAAQ;KACN,EAAE,MAAM,EAAE,eAAe,YAAY,EAAE;KACvC,EAAE,MAAM,EAAE,qBAAqB,KAAK,EAAE;KACtC,EAAE,QAAQ,EAAE,OAAO,wBAAwB,EAAE;IAC/C,EACF,EACF;IACA,SAAS;KACP;KACA;KACA;KACA;KACA;KACA;KACA;IACF;IACA,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC;GACzB;GAEA,YACE,MAAM,KAAK,KAAK,SACd,WACA,EAAE,KAAK,GACP,6BACF,EAAA,CACA;EACJ,SAAS,KAAK;GACZ,KAAK,oBAAoB,GAAG;EAC9B;EAEA,IAAI,qBAAoC;EACxC,IAAI,gBAA+B;;EAKnC,IAAI,UACF,KAAK,MAAM,WAAW,UAAU;GAC9B,uBAAuB,QAAQ;GAC/B,IAAI,CAAC,iBAAiB,QAAQ,UAC5B,gBAAgB,QAAQ;EAE5B;EAEF,IAAI,SAAS,SAAS,KAAK,oBAAoB;GAC7C,SAAS;IACP;IACA,cAAc,6BAA6B,mBAAmB;IAC9D,UAAU,4BAA4B;GACxC;GAEA,IAAI,eAAe;IACjB,OAAO,SAAS,CAAC;IACjB,OAAO,KAAK,SAAS;GACvB;EACF;EAEA,OAAO;CACT;CAEA,YACE,QAC+B;EAC/B,OAAO,UACL;GACE,WAAW,cAAc,eAAe;GACxC,KAAK,OAAO;GACZ,QAAQ;GACR,UAAU;EACZ,SACM,KAAK,aAAa,MAAM,CAChC;CACF;AACF"}
@@ -74,7 +74,7 @@ var CrateDatasource = class CrateDatasource extends Datasource {
74
74
  return withCache({
75
75
  namespace: `datasource-${CrateDatasource.id}`,
76
76
  key: buildCacheKey(config.registryUrl, config.packageName),
77
- cacheable: CrateDatasource.isCratesIo(config.registryUrl),
77
+ public: CrateDatasource.isCratesIo(config.registryUrl),
78
78
  fallback: true
79
79
  }, () => this._getReleases(config));
80
80
  }
@@ -183,7 +183,7 @@ var CrateDatasource = class CrateDatasource extends Datasource {
183
183
  return withCache({
184
184
  namespace: `datasource-${CrateDatasource.id}-metadata`,
185
185
  key: buildCacheKey(info.rawUrl, packageName),
186
- cacheable: info.flavor === "crates.io",
186
+ public: info.flavor === "crates.io",
187
187
  ttlMinutes: 1440
188
188
  }, () => this._getCrateMetadata(info, packageName));
189
189
  }
@@ -389,7 +389,7 @@ var CrateDatasource = class CrateDatasource extends Datasource {
389
389
  namespace: `datasource-crate`,
390
390
  key: buildCacheKey("postprocessRelease", config.registryUrl, config.packageName, release.version),
391
391
  ttlMinutes: 10080,
392
- cacheable: CrateDatasource.isCratesIo(config.registryUrl ?? void 0)
392
+ public: CrateDatasource.isCratesIo(config.registryUrl ?? void 0)
393
393
  }, () => this._postprocessRelease(config, release));
394
394
  }
395
395
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["cargoVersioning.id","memCache.get","HttpError","packageCache.get","packageCache.set"],"sources":["../../../../lib/modules/datasource/crate/index.ts"],"sourcesContent":["import { isBoolean } from '@sindresorhus/is';\nimport upath from 'upath';\nimport { GlobalConfig } from '../../../config/global.ts';\nimport { logger } from '../../../logger/index.ts';\nimport * as memCache from '../../../util/cache/memory/index.ts';\nimport * as packageCache from '../../../util/cache/package/index.ts';\nimport { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { privateCacheDir, readCacheFile } from '../../../util/fs/index.ts';\nimport { createSimpleGit } from '../../../util/git/index.ts';\nimport { toSha256 } from '../../../util/hash.ts';\nimport { memCacheProvider } from '../../../util/http/cache/memory-http-cache-provider.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport { acquireLock } from '../../../util/mutex.ts';\nimport { newlineRegex, regEx } from '../../../util/regex.ts';\nimport { Json } from '../../../util/schema-utils/index.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport { isHttpUrl, joinUrlParts, parseUrl } from '../../../util/url.ts';\nimport * as cargoVersioning from '../../versioning/cargo/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type {\n GetReleasesConfig,\n PostprocessReleaseConfig,\n PostprocessReleaseResult,\n Release,\n ReleaseResult,\n} from '../types.ts';\nimport type { CrateMetadata } from './schema.ts';\nimport {\n CrateMetadataResponse,\n RegistryConfig,\n ReleaseTimestamp,\n} from './schema.ts';\nimport type { CrateRecord, RegistryFlavor, RegistryInfo } from './types.ts';\n\ntype CloneResult =\n | {\n err: Error;\n clonePath?: undefined;\n }\n | {\n clonePath: string;\n err?: undefined;\n };\n\nexport class CrateDatasource extends Datasource {\n static readonly id = 'crate';\n\n constructor() {\n super(CrateDatasource.id);\n }\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return ['sparse+https://index.crates.io/'];\n }\n\n override defaultVersioning = cargoVersioning.id;\n\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `repository` field in the results.';\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from `pubtime` field from crates.io index if available, or `version.created_at` field from crates.io API otherwise.';\n\n private async _getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore if -- should never happen */\n if (!registryUrl) {\n logger.warn(\n 'crate datasource: No registryUrl specified, cannot perform getReleases',\n );\n return null;\n }\n\n const registryInfo = await CrateDatasource.fetchRegistryInfo({\n packageName,\n registryUrl,\n });\n if (!registryInfo) {\n logger.debug(`Could not fetch registry info from ${registryUrl}`);\n return null;\n }\n\n const dependencyUrl = CrateDatasource.getDependencyUrl(\n registryInfo,\n packageName,\n );\n\n const payload = await this.fetchCrateRecordsPayload(\n registryInfo,\n packageName,\n );\n const lines = payload\n .split(newlineRegex) // break into lines\n .map((line) => line.trim()) // remove whitespace\n .filter((line) => line.length !== 0) // remove empty lines\n .map((line) => JSON.parse(line) as CrateRecord); // parse\n\n const metadata = await this.getCrateMetadata(registryInfo, packageName);\n\n const result: ReleaseResult = {\n dependencyUrl,\n releases: [],\n };\n\n if (metadata?.homepage) {\n result.homepage = metadata.homepage;\n }\n\n if (metadata?.repository) {\n result.sourceUrl = metadata.repository;\n }\n\n result.releases = lines\n .map((line) => {\n const versionOrig = line.vers;\n const version = versionOrig.replace(regEx(/\\+.*$/), '');\n const release: Release = { version };\n\n if (versionOrig !== version) {\n release.versionOrig = versionOrig;\n }\n\n if (line.yanked) {\n release.isDeprecated = true;\n }\n\n if (line.rust_version) {\n release.constraints = { rust: [line.rust_version] };\n }\n\n if (line.pubtime) {\n release.releaseTimestamp = asTimestamp(line.pubtime);\n }\n\n return release;\n })\n .filter((release) => release.version);\n if (!result.releases.length) {\n return null;\n }\n\n return result;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${CrateDatasource.id}`,\n key: buildCacheKey(config.registryUrl, config.packageName),\n cacheable: CrateDatasource.isCratesIo(config.registryUrl),\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n\n /** Returns the memory cache key of the registry's config.json. */\n private static registryConfigCacheKey(rawUrl: string): string {\n return buildCacheKey('crate-datasource', 'registry-config', rawUrl);\n }\n\n /** Returns the memory cache key of the unsupported read API marker. */\n private static apiUnsupportedCacheKey(api: string): string {\n return buildCacheKey('crate-datasource', 'registry-api-unsupported', api);\n }\n\n /**\n * Fetches the registry's config.json which provides the API base URL\n * and download URL template.\n * See: https://doc.rust-lang.org/cargo/reference/registry-index.html#index-configuration\n */\n private async fetchRegistryConfig(\n info: RegistryInfo,\n ): Promise<RegistryConfig | null> {\n const cacheKey = CrateDatasource.registryConfigCacheKey(info.rawUrl);\n const cached = memCache.get<RegistryConfig>(cacheKey);\n if (cached) {\n return cached;\n }\n\n if (info.clonePath) {\n try {\n const configPath = upath.join(info.clonePath, 'config.json');\n const content = await readCacheFile(configPath, 'utf8');\n const parsed = Json.pipe(RegistryConfig).parse(content);\n memCache.set(cacheKey, parsed);\n return parsed;\n } catch {\n logger.debug(\n { registryUrl: info.rawUrl },\n 'Could not read config.json from cloned registry',\n );\n }\n } else {\n try {\n const configUrl = joinUrlParts(info.rawUrl, 'config.json');\n const { body } = await this.http.getJson(configUrl, RegistryConfig);\n memCache.set(cacheKey, body);\n return body;\n } catch {\n logger.debug(\n { registryUrl: info.rawUrl },\n 'Could not fetch registry config.json',\n );\n }\n }\n\n return null;\n }\n\n /**\n * The registry web API specification only defines publish, yank, unyank,\n * owners, search and login endpoints, see\n * https://doc.rust-lang.org/cargo/reference/registry-web-api.html.\n * `GET api/v1/crates/<name>` and `GET api/v1/crates/<name>/<version>` are\n * crates.io extensions. Registries which mirror the crates.io index keep\n * `api` pointing at crates.io and are used without probing. Any other\n * registry is probed once and remembered as unsupported when it answers\n * 404, since private registries (Artifactory, CodeArtifact, ...) advertise\n * an `api` URL for publishing only.\n */\n private static isCratesIoApi(api: string): boolean {\n return parseUrl(api)?.hostname === 'crates.io';\n }\n\n private static isNotFound(err: unknown): boolean {\n return err instanceof HttpError && err.response?.statusCode === 404;\n }\n\n /**\n * The marker is keyed by the `api` URL, since the missing endpoints are a\n * property of the API server, not of the index that points to it.\n */\n private async isReadApiUnsupported(api: string): Promise<boolean> {\n if (CrateDatasource.isCratesIoApi(api)) {\n return false;\n }\n\n const memKey = CrateDatasource.apiUnsupportedCacheKey(api);\n const cached = memCache.get<boolean>(memKey);\n if (isBoolean(cached)) {\n return cached;\n }\n\n const persisted = await packageCache.get<boolean>(\n 'datasource-crate-registry-api',\n api,\n );\n const unsupported = persisted === true;\n memCache.set(memKey, unsupported);\n return unsupported;\n }\n\n private async markReadApiUnsupported(\n registryUrl: string,\n api: string,\n ): Promise<void> {\n logger.debug(\n { registryUrl, api },\n 'Registry does not implement the crates.io read API, skipping crate metadata and release timestamp lookups',\n );\n memCache.set(CrateDatasource.apiUnsupportedCacheKey(api), true);\n await packageCache.set('datasource-crate-registry-api', api, true, 24 * 60);\n }\n\n private async _getCrateMetadata(\n info: RegistryInfo,\n packageName: string,\n ): Promise<CrateMetadata | null> {\n const registryConfig = await this.fetchRegistryConfig(info);\n if (!registryConfig?.api) {\n return null;\n }\n\n const { api } = registryConfig;\n if (await this.isReadApiUnsupported(api)) {\n return null;\n }\n\n const apiBaseUrl = joinUrlParts(api, 'api/v1/');\n\n // The `?include=` suffix is required to avoid unnecessary database queries\n // on the crates.io server. This lets us work around the regular request\n // throttling of one request per second.\n const crateUrl = `${apiBaseUrl}crates/${packageName}?include=`;\n\n logger.trace(\n { crateUrl, packageName, registryUrl: info.rawUrl },\n 'downloading crate metadata',\n );\n\n try {\n const { body } = await this.http.getJson(crateUrl, CrateMetadataResponse);\n return body.crate;\n } catch (err) {\n // The index lookup preceding this call found the crate, so a 404 means\n // the registry does not implement the endpoint at all\n if (\n !CrateDatasource.isCratesIoApi(api) &&\n CrateDatasource.isNotFound(err)\n ) {\n await this.markReadApiUnsupported(info.rawUrl, api);\n return null;\n }\n logger.debug(\n { err, packageName, registryUrl: info.rawUrl },\n 'failed to download crate metadata',\n );\n }\n\n return null;\n }\n\n public getCrateMetadata(\n info: RegistryInfo,\n packageName: string,\n ): Promise<CrateMetadata | null> {\n return withCache(\n {\n namespace: `datasource-${CrateDatasource.id}-metadata`,\n key: buildCacheKey(info.rawUrl, packageName),\n cacheable: info.flavor === 'crates.io',\n ttlMinutes: 24 * 60, // 24 hours\n },\n () => this._getCrateMetadata(info, packageName),\n );\n }\n\n public async fetchCrateRecordsPayload(\n info: RegistryInfo,\n packageName: string,\n ): Promise<string> {\n if (info.clonePath) {\n const path = upath.join(\n info.clonePath,\n ...CrateDatasource.getIndexSuffix(packageName),\n );\n return readCacheFile(path, 'utf8');\n }\n\n // sparse index\n const packageSuffix = CrateDatasource.getIndexSuffix(\n packageName.toLowerCase(),\n );\n const crateUrl = joinUrlParts(info.rawUrl, ...packageSuffix);\n logger.trace(\n { crateUrl, packageName, registryUrl: info.rawUrl },\n 'fetching crate records from sparse index',\n );\n try {\n return (await this.http.getText(crateUrl)).body;\n } catch (err) {\n this.handleGenericErrors(err);\n }\n }\n\n /**\n * Computes the dependency URL for a crate, given\n * registry information\n */\n private static getDependencyUrl(\n info: RegistryInfo,\n packageName: string,\n ): string {\n switch (info.flavor) {\n case 'crates.io':\n return `https://crates.io/crates/${packageName}`;\n case 'cloudsmith': {\n // input: https://dl.cloudsmith.io/basic/$org/$repo/cargo/index.git\n const tokens = info.url.pathname.split('/');\n const org = tokens[2];\n const repo = tokens[3];\n return `https://cloudsmith.io/~${org}/repos/${repo}/packages/detail/cargo/${packageName}`;\n }\n default:\n return `${info.url}/${packageName}`;\n }\n }\n\n /**\n * Given a Git URL, computes a semi-human-readable name for a folder in which to\n * clone the repository.\n */\n private static cacheDirFromUrl(url: URL): string {\n const proto = url.protocol.replace(regEx(/:$/), '');\n const host = url.hostname;\n const hash = toSha256(url.pathname).substring(0, 7);\n\n return `crate-registry-${proto}-${host}-${hash}`;\n }\n\n private static isSparseRegistry(url: string): boolean {\n const parsed = parseUrl(url);\n if (!parsed) {\n return false;\n }\n return parsed.protocol.startsWith('sparse+');\n }\n\n /**\n * Fetches information about a registry, by url.\n * If no url is given, assumes crates.io.\n * If an url is given, assumes it's a valid Git repository\n * url and clones it to cache.\n */\n private static async fetchRegistryInfo({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<RegistryInfo | null> {\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n const isSparseRegistry = CrateDatasource.isSparseRegistry(registryUrl);\n const registryFetchUrl = isSparseRegistry\n ? registryUrl.replace(regEx(/^sparse\\+/), '')\n : registryUrl;\n\n const url = parseUrl(registryFetchUrl);\n if (!url) {\n logger.debug(`Could not parse registry URL ${registryFetchUrl}`);\n return null;\n }\n\n let flavor: RegistryFlavor;\n if (url.hostname === 'index.crates.io') {\n flavor = 'crates.io';\n } else if (url.hostname === 'dl.cloudsmith.io') {\n flavor = 'cloudsmith';\n } else {\n flavor = 'other';\n }\n\n const registry: RegistryInfo = {\n flavor,\n rawUrl: registryFetchUrl,\n url,\n };\n\n if (!isSparseRegistry) {\n if (\n registry.flavor !== 'crates.io' &&\n !GlobalConfig.get('allowCustomCrateGitRegistries')\n ) {\n logger.warn(\n 'crate datasource: allowCustomCrateGitRegistries=true is required for git-cloning registries other than crates.io, bailing out',\n );\n return null;\n }\n const cacheKey = buildCacheKey(\n 'crate-datasource',\n 'registry-clone-path',\n registryFetchUrl,\n );\n const lockKey = registryFetchUrl;\n\n const executionTimeout = GlobalConfig.get('executionTimeout') * 60 * 1000;\n const gitTimeout = GlobalConfig.get('gitTimeout') || executionTimeout;\n const releaseLock = await acquireLock(\n lockKey,\n 'crate-registry',\n gitTimeout,\n );\n try {\n const cached = memCache.get<CloneResult>(cacheKey);\n\n if (cached?.err) {\n logger.warn(\n { err: cached.err, packageName, registryFetchUrl },\n 'Previous git clone failed, bailing out.',\n );\n return null;\n }\n\n if (cached?.clonePath) {\n registry.clonePath = cached.clonePath;\n return registry;\n }\n\n const clonePath = upath.join(\n privateCacheDir(),\n CrateDatasource.cacheDirFromUrl(url),\n );\n\n const result = await CrateDatasource.clone(\n registryFetchUrl,\n clonePath,\n packageName,\n );\n\n memCache.set(cacheKey, result);\n\n if (result.err) {\n logger.warn(\n { err: result.err, packageName, registryFetchUrl },\n 'Git clone failed, bailing out.',\n );\n return null;\n }\n\n registry.clonePath = result.clonePath;\n } finally {\n releaseLock();\n }\n }\n\n return registry;\n }\n\n private static async clone(\n registryFetchUrl: string,\n clonePath: string,\n packageName: string,\n ): Promise<CloneResult> {\n logger.info(\n { clonePath, registryFetchUrl },\n `Cloning private cargo registry`,\n );\n\n const git = createSimpleGit({\n config: { maxConcurrentProcesses: 1 },\n ...(isHttpUrl(registryFetchUrl) && {\n // the URL as well, for the credentials registered from it, which only apply to this registry\n authentication: {\n hostTypes: [CrateDatasource.id],\n url: registryFetchUrl,\n },\n }),\n });\n\n try {\n await git.clone(registryFetchUrl, clonePath, {\n '--depth': 1,\n });\n return { clonePath };\n } catch (err) {\n if (\n err.message.includes(\n 'fatal: dumb http transport does not support shallow capabilities',\n )\n ) {\n logger.info(\n { packageName, registryFetchUrl },\n 'failed to shallow clone git registry, doing full clone',\n );\n try {\n await git.clone(registryFetchUrl, clonePath);\n return { clonePath };\n } catch (err) {\n logger.warn(\n { err, packageName, registryFetchUrl },\n 'failed cloning git registry',\n );\n return { err };\n }\n } else {\n logger.warn(\n { err, packageName, registryFetchUrl },\n 'failed cloning git registry',\n );\n return { err };\n }\n }\n }\n\n private static isCratesIo(registryUrl: string | undefined): boolean {\n if (!registryUrl) {\n return false;\n }\n const normalized = registryUrl.replace(regEx(/^sparse\\+/), '');\n const parsed = parseUrl(normalized);\n return parsed?.hostname === 'index.crates.io';\n }\n\n public static getIndexSuffix(packageName: string): string[] {\n const len = packageName.length;\n\n if (len === 1) {\n return ['1', packageName];\n }\n if (len === 2) {\n return ['2', packageName];\n }\n if (len === 3) {\n return ['3', packageName[0], packageName];\n }\n\n return [packageName.slice(0, 2), packageName.slice(2, 4), packageName];\n }\n\n private async _postprocessRelease(\n { packageName, registryUrl }: PostprocessReleaseConfig,\n release: Release,\n ): Promise<PostprocessReleaseResult> {\n if (release.releaseTimestamp) {\n return release;\n }\n\n // Look up the registry config from cache (populated during getReleases)\n const rawUrl = registryUrl?.replace(regEx(/^sparse\\+/), '');\n if (!rawUrl) {\n return release;\n }\n const config = memCache.get<RegistryConfig>(\n CrateDatasource.registryConfigCacheKey(rawUrl),\n );\n if (!config?.api) {\n return release;\n }\n\n const { api } = config;\n if (await this.isReadApiUnsupported(api)) {\n return release;\n }\n\n const apiBaseUrl = joinUrlParts(api, 'api/v1/');\n const url = `${apiBaseUrl}crates/${packageName}/${release.versionOrig ?? release.version}`;\n logger.trace(\n { url, packageName, version: release.version, registryUrl },\n 'fetching crate release timestamp',\n );\n // Getting release timestamp could become unnecessary if the manual backfill of `pubtime` mentioned in\n // https://github.com/rust-lang/cargo/issues/15491 is done for all packages.\n try {\n const { body: releaseTimestamp } = await this.http.getJson(\n url,\n { cacheProvider: memCacheProvider },\n ReleaseTimestamp,\n );\n release.releaseTimestamp = releaseTimestamp;\n } catch (err) {\n if (\n CrateDatasource.isCratesIoApi(api) ||\n !CrateDatasource.isNotFound(err)\n ) {\n throw err;\n }\n await this.markReadApiUnsupported(rawUrl, api);\n }\n return release;\n }\n\n override postprocessRelease(\n config: PostprocessReleaseConfig,\n release: Release,\n ): Promise<PostprocessReleaseResult> {\n return withCache(\n {\n namespace: `datasource-crate`,\n key: buildCacheKey(\n 'postprocessRelease',\n config.registryUrl,\n config.packageName,\n release.version,\n ),\n ttlMinutes: 7 * 24 * 60,\n cacheable: CrateDatasource.isCratesIo(config.registryUrl ?? undefined),\n },\n () => this._postprocessRelease(config, release),\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["cargoVersioning.id","memCache.get","HttpError","packageCache.get","packageCache.set"],"sources":["../../../../lib/modules/datasource/crate/index.ts"],"sourcesContent":["import { isBoolean } from '@sindresorhus/is';\nimport upath from 'upath';\nimport { GlobalConfig } from '../../../config/global.ts';\nimport { logger } from '../../../logger/index.ts';\nimport * as memCache from '../../../util/cache/memory/index.ts';\nimport * as packageCache from '../../../util/cache/package/index.ts';\nimport { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { privateCacheDir, readCacheFile } from '../../../util/fs/index.ts';\nimport { createSimpleGit } from '../../../util/git/index.ts';\nimport { toSha256 } from '../../../util/hash.ts';\nimport { memCacheProvider } from '../../../util/http/cache/memory-http-cache-provider.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport { acquireLock } from '../../../util/mutex.ts';\nimport { newlineRegex, regEx } from '../../../util/regex.ts';\nimport { Json } from '../../../util/schema-utils/index.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport { isHttpUrl, joinUrlParts, parseUrl } from '../../../util/url.ts';\nimport * as cargoVersioning from '../../versioning/cargo/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type {\n GetReleasesConfig,\n PostprocessReleaseConfig,\n PostprocessReleaseResult,\n Release,\n ReleaseResult,\n} from '../types.ts';\nimport type { CrateMetadata } from './schema.ts';\nimport {\n CrateMetadataResponse,\n RegistryConfig,\n ReleaseTimestamp,\n} from './schema.ts';\nimport type { CrateRecord, RegistryFlavor, RegistryInfo } from './types.ts';\n\ntype CloneResult =\n | {\n err: Error;\n clonePath?: undefined;\n }\n | {\n clonePath: string;\n err?: undefined;\n };\n\nexport class CrateDatasource extends Datasource {\n static readonly id = 'crate';\n\n constructor() {\n super(CrateDatasource.id);\n }\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return ['sparse+https://index.crates.io/'];\n }\n\n override defaultVersioning = cargoVersioning.id;\n\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `repository` field in the results.';\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from `pubtime` field from crates.io index if available, or `version.created_at` field from crates.io API otherwise.';\n\n private async _getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore if -- should never happen */\n if (!registryUrl) {\n logger.warn(\n 'crate datasource: No registryUrl specified, cannot perform getReleases',\n );\n return null;\n }\n\n const registryInfo = await CrateDatasource.fetchRegistryInfo({\n packageName,\n registryUrl,\n });\n if (!registryInfo) {\n logger.debug(`Could not fetch registry info from ${registryUrl}`);\n return null;\n }\n\n const dependencyUrl = CrateDatasource.getDependencyUrl(\n registryInfo,\n packageName,\n );\n\n const payload = await this.fetchCrateRecordsPayload(\n registryInfo,\n packageName,\n );\n const lines = payload\n .split(newlineRegex) // break into lines\n .map((line) => line.trim()) // remove whitespace\n .filter((line) => line.length !== 0) // remove empty lines\n .map((line) => JSON.parse(line) as CrateRecord); // parse\n\n const metadata = await this.getCrateMetadata(registryInfo, packageName);\n\n const result: ReleaseResult = {\n dependencyUrl,\n releases: [],\n };\n\n if (metadata?.homepage) {\n result.homepage = metadata.homepage;\n }\n\n if (metadata?.repository) {\n result.sourceUrl = metadata.repository;\n }\n\n result.releases = lines\n .map((line) => {\n const versionOrig = line.vers;\n const version = versionOrig.replace(regEx(/\\+.*$/), '');\n const release: Release = { version };\n\n if (versionOrig !== version) {\n release.versionOrig = versionOrig;\n }\n\n if (line.yanked) {\n release.isDeprecated = true;\n }\n\n if (line.rust_version) {\n release.constraints = { rust: [line.rust_version] };\n }\n\n if (line.pubtime) {\n release.releaseTimestamp = asTimestamp(line.pubtime);\n }\n\n return release;\n })\n .filter((release) => release.version);\n if (!result.releases.length) {\n return null;\n }\n\n return result;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${CrateDatasource.id}`,\n key: buildCacheKey(config.registryUrl, config.packageName),\n public: CrateDatasource.isCratesIo(config.registryUrl),\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n\n /** Returns the memory cache key of the registry's config.json. */\n private static registryConfigCacheKey(rawUrl: string): string {\n return buildCacheKey('crate-datasource', 'registry-config', rawUrl);\n }\n\n /** Returns the memory cache key of the unsupported read API marker. */\n private static apiUnsupportedCacheKey(api: string): string {\n return buildCacheKey('crate-datasource', 'registry-api-unsupported', api);\n }\n\n /**\n * Fetches the registry's config.json which provides the API base URL\n * and download URL template.\n * See: https://doc.rust-lang.org/cargo/reference/registry-index.html#index-configuration\n */\n private async fetchRegistryConfig(\n info: RegistryInfo,\n ): Promise<RegistryConfig | null> {\n const cacheKey = CrateDatasource.registryConfigCacheKey(info.rawUrl);\n const cached = memCache.get<RegistryConfig>(cacheKey);\n if (cached) {\n return cached;\n }\n\n if (info.clonePath) {\n try {\n const configPath = upath.join(info.clonePath, 'config.json');\n const content = await readCacheFile(configPath, 'utf8');\n const parsed = Json.pipe(RegistryConfig).parse(content);\n memCache.set(cacheKey, parsed);\n return parsed;\n } catch {\n logger.debug(\n { registryUrl: info.rawUrl },\n 'Could not read config.json from cloned registry',\n );\n }\n } else {\n try {\n const configUrl = joinUrlParts(info.rawUrl, 'config.json');\n const { body } = await this.http.getJson(configUrl, RegistryConfig);\n memCache.set(cacheKey, body);\n return body;\n } catch {\n logger.debug(\n { registryUrl: info.rawUrl },\n 'Could not fetch registry config.json',\n );\n }\n }\n\n return null;\n }\n\n /**\n * The registry web API specification only defines publish, yank, unyank,\n * owners, search and login endpoints, see\n * https://doc.rust-lang.org/cargo/reference/registry-web-api.html.\n * `GET api/v1/crates/<name>` and `GET api/v1/crates/<name>/<version>` are\n * crates.io extensions. Registries which mirror the crates.io index keep\n * `api` pointing at crates.io and are used without probing. Any other\n * registry is probed once and remembered as unsupported when it answers\n * 404, since private registries (Artifactory, CodeArtifact, ...) advertise\n * an `api` URL for publishing only.\n */\n private static isCratesIoApi(api: string): boolean {\n return parseUrl(api)?.hostname === 'crates.io';\n }\n\n private static isNotFound(err: unknown): boolean {\n return err instanceof HttpError && err.response?.statusCode === 404;\n }\n\n /**\n * The marker is keyed by the `api` URL, since the missing endpoints are a\n * property of the API server, not of the index that points to it.\n */\n private async isReadApiUnsupported(api: string): Promise<boolean> {\n if (CrateDatasource.isCratesIoApi(api)) {\n return false;\n }\n\n const memKey = CrateDatasource.apiUnsupportedCacheKey(api);\n const cached = memCache.get<boolean>(memKey);\n if (isBoolean(cached)) {\n return cached;\n }\n\n const persisted = await packageCache.get<boolean>(\n 'datasource-crate-registry-api',\n api,\n );\n const unsupported = persisted === true;\n memCache.set(memKey, unsupported);\n return unsupported;\n }\n\n private async markReadApiUnsupported(\n registryUrl: string,\n api: string,\n ): Promise<void> {\n logger.debug(\n { registryUrl, api },\n 'Registry does not implement the crates.io read API, skipping crate metadata and release timestamp lookups',\n );\n memCache.set(CrateDatasource.apiUnsupportedCacheKey(api), true);\n await packageCache.set('datasource-crate-registry-api', api, true, 24 * 60);\n }\n\n private async _getCrateMetadata(\n info: RegistryInfo,\n packageName: string,\n ): Promise<CrateMetadata | null> {\n const registryConfig = await this.fetchRegistryConfig(info);\n if (!registryConfig?.api) {\n return null;\n }\n\n const { api } = registryConfig;\n if (await this.isReadApiUnsupported(api)) {\n return null;\n }\n\n const apiBaseUrl = joinUrlParts(api, 'api/v1/');\n\n // The `?include=` suffix is required to avoid unnecessary database queries\n // on the crates.io server. This lets us work around the regular request\n // throttling of one request per second.\n const crateUrl = `${apiBaseUrl}crates/${packageName}?include=`;\n\n logger.trace(\n { crateUrl, packageName, registryUrl: info.rawUrl },\n 'downloading crate metadata',\n );\n\n try {\n const { body } = await this.http.getJson(crateUrl, CrateMetadataResponse);\n return body.crate;\n } catch (err) {\n // The index lookup preceding this call found the crate, so a 404 means\n // the registry does not implement the endpoint at all\n if (\n !CrateDatasource.isCratesIoApi(api) &&\n CrateDatasource.isNotFound(err)\n ) {\n await this.markReadApiUnsupported(info.rawUrl, api);\n return null;\n }\n logger.debug(\n { err, packageName, registryUrl: info.rawUrl },\n 'failed to download crate metadata',\n );\n }\n\n return null;\n }\n\n public getCrateMetadata(\n info: RegistryInfo,\n packageName: string,\n ): Promise<CrateMetadata | null> {\n return withCache(\n {\n namespace: `datasource-${CrateDatasource.id}-metadata`,\n key: buildCacheKey(info.rawUrl, packageName),\n public: info.flavor === 'crates.io',\n ttlMinutes: 24 * 60, // 24 hours\n },\n () => this._getCrateMetadata(info, packageName),\n );\n }\n\n public async fetchCrateRecordsPayload(\n info: RegistryInfo,\n packageName: string,\n ): Promise<string> {\n if (info.clonePath) {\n const path = upath.join(\n info.clonePath,\n ...CrateDatasource.getIndexSuffix(packageName),\n );\n return readCacheFile(path, 'utf8');\n }\n\n // sparse index\n const packageSuffix = CrateDatasource.getIndexSuffix(\n packageName.toLowerCase(),\n );\n const crateUrl = joinUrlParts(info.rawUrl, ...packageSuffix);\n logger.trace(\n { crateUrl, packageName, registryUrl: info.rawUrl },\n 'fetching crate records from sparse index',\n );\n try {\n return (await this.http.getText(crateUrl)).body;\n } catch (err) {\n this.handleGenericErrors(err);\n }\n }\n\n /**\n * Computes the dependency URL for a crate, given\n * registry information\n */\n private static getDependencyUrl(\n info: RegistryInfo,\n packageName: string,\n ): string {\n switch (info.flavor) {\n case 'crates.io':\n return `https://crates.io/crates/${packageName}`;\n case 'cloudsmith': {\n // input: https://dl.cloudsmith.io/basic/$org/$repo/cargo/index.git\n const tokens = info.url.pathname.split('/');\n const org = tokens[2];\n const repo = tokens[3];\n return `https://cloudsmith.io/~${org}/repos/${repo}/packages/detail/cargo/${packageName}`;\n }\n default:\n return `${info.url}/${packageName}`;\n }\n }\n\n /**\n * Given a Git URL, computes a semi-human-readable name for a folder in which to\n * clone the repository.\n */\n private static cacheDirFromUrl(url: URL): string {\n const proto = url.protocol.replace(regEx(/:$/), '');\n const host = url.hostname;\n const hash = toSha256(url.pathname).substring(0, 7);\n\n return `crate-registry-${proto}-${host}-${hash}`;\n }\n\n private static isSparseRegistry(url: string): boolean {\n const parsed = parseUrl(url);\n if (!parsed) {\n return false;\n }\n return parsed.protocol.startsWith('sparse+');\n }\n\n /**\n * Fetches information about a registry, by url.\n * If no url is given, assumes crates.io.\n * If an url is given, assumes it's a valid Git repository\n * url and clones it to cache.\n */\n private static async fetchRegistryInfo({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<RegistryInfo | null> {\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n const isSparseRegistry = CrateDatasource.isSparseRegistry(registryUrl);\n const registryFetchUrl = isSparseRegistry\n ? registryUrl.replace(regEx(/^sparse\\+/), '')\n : registryUrl;\n\n const url = parseUrl(registryFetchUrl);\n if (!url) {\n logger.debug(`Could not parse registry URL ${registryFetchUrl}`);\n return null;\n }\n\n let flavor: RegistryFlavor;\n if (url.hostname === 'index.crates.io') {\n flavor = 'crates.io';\n } else if (url.hostname === 'dl.cloudsmith.io') {\n flavor = 'cloudsmith';\n } else {\n flavor = 'other';\n }\n\n const registry: RegistryInfo = {\n flavor,\n rawUrl: registryFetchUrl,\n url,\n };\n\n if (!isSparseRegistry) {\n if (\n registry.flavor !== 'crates.io' &&\n !GlobalConfig.get('allowCustomCrateGitRegistries')\n ) {\n logger.warn(\n 'crate datasource: allowCustomCrateGitRegistries=true is required for git-cloning registries other than crates.io, bailing out',\n );\n return null;\n }\n const cacheKey = buildCacheKey(\n 'crate-datasource',\n 'registry-clone-path',\n registryFetchUrl,\n );\n const lockKey = registryFetchUrl;\n\n const executionTimeout = GlobalConfig.get('executionTimeout') * 60 * 1000;\n const gitTimeout = GlobalConfig.get('gitTimeout') || executionTimeout;\n const releaseLock = await acquireLock(\n lockKey,\n 'crate-registry',\n gitTimeout,\n );\n try {\n const cached = memCache.get<CloneResult>(cacheKey);\n\n if (cached?.err) {\n logger.warn(\n { err: cached.err, packageName, registryFetchUrl },\n 'Previous git clone failed, bailing out.',\n );\n return null;\n }\n\n if (cached?.clonePath) {\n registry.clonePath = cached.clonePath;\n return registry;\n }\n\n const clonePath = upath.join(\n privateCacheDir(),\n CrateDatasource.cacheDirFromUrl(url),\n );\n\n const result = await CrateDatasource.clone(\n registryFetchUrl,\n clonePath,\n packageName,\n );\n\n memCache.set(cacheKey, result);\n\n if (result.err) {\n logger.warn(\n { err: result.err, packageName, registryFetchUrl },\n 'Git clone failed, bailing out.',\n );\n return null;\n }\n\n registry.clonePath = result.clonePath;\n } finally {\n releaseLock();\n }\n }\n\n return registry;\n }\n\n private static async clone(\n registryFetchUrl: string,\n clonePath: string,\n packageName: string,\n ): Promise<CloneResult> {\n logger.info(\n { clonePath, registryFetchUrl },\n `Cloning private cargo registry`,\n );\n\n const git = createSimpleGit({\n config: { maxConcurrentProcesses: 1 },\n ...(isHttpUrl(registryFetchUrl) && {\n // the URL as well, for the credentials registered from it, which only apply to this registry\n authentication: {\n hostTypes: [CrateDatasource.id],\n url: registryFetchUrl,\n },\n }),\n });\n\n try {\n await git.clone(registryFetchUrl, clonePath, {\n '--depth': 1,\n });\n return { clonePath };\n } catch (err) {\n if (\n err.message.includes(\n 'fatal: dumb http transport does not support shallow capabilities',\n )\n ) {\n logger.info(\n { packageName, registryFetchUrl },\n 'failed to shallow clone git registry, doing full clone',\n );\n try {\n await git.clone(registryFetchUrl, clonePath);\n return { clonePath };\n } catch (err) {\n logger.warn(\n { err, packageName, registryFetchUrl },\n 'failed cloning git registry',\n );\n return { err };\n }\n } else {\n logger.warn(\n { err, packageName, registryFetchUrl },\n 'failed cloning git registry',\n );\n return { err };\n }\n }\n }\n\n private static isCratesIo(registryUrl: string | undefined): boolean {\n if (!registryUrl) {\n return false;\n }\n const normalized = registryUrl.replace(regEx(/^sparse\\+/), '');\n const parsed = parseUrl(normalized);\n return parsed?.hostname === 'index.crates.io';\n }\n\n public static getIndexSuffix(packageName: string): string[] {\n const len = packageName.length;\n\n if (len === 1) {\n return ['1', packageName];\n }\n if (len === 2) {\n return ['2', packageName];\n }\n if (len === 3) {\n return ['3', packageName[0], packageName];\n }\n\n return [packageName.slice(0, 2), packageName.slice(2, 4), packageName];\n }\n\n private async _postprocessRelease(\n { packageName, registryUrl }: PostprocessReleaseConfig,\n release: Release,\n ): Promise<PostprocessReleaseResult> {\n if (release.releaseTimestamp) {\n return release;\n }\n\n // Look up the registry config from cache (populated during getReleases)\n const rawUrl = registryUrl?.replace(regEx(/^sparse\\+/), '');\n if (!rawUrl) {\n return release;\n }\n const config = memCache.get<RegistryConfig>(\n CrateDatasource.registryConfigCacheKey(rawUrl),\n );\n if (!config?.api) {\n return release;\n }\n\n const { api } = config;\n if (await this.isReadApiUnsupported(api)) {\n return release;\n }\n\n const apiBaseUrl = joinUrlParts(api, 'api/v1/');\n const url = `${apiBaseUrl}crates/${packageName}/${release.versionOrig ?? release.version}`;\n logger.trace(\n { url, packageName, version: release.version, registryUrl },\n 'fetching crate release timestamp',\n );\n // Getting release timestamp could become unnecessary if the manual backfill of `pubtime` mentioned in\n // https://github.com/rust-lang/cargo/issues/15491 is done for all packages.\n try {\n const { body: releaseTimestamp } = await this.http.getJson(\n url,\n { cacheProvider: memCacheProvider },\n ReleaseTimestamp,\n );\n release.releaseTimestamp = releaseTimestamp;\n } catch (err) {\n if (\n CrateDatasource.isCratesIoApi(api) ||\n !CrateDatasource.isNotFound(err)\n ) {\n throw err;\n }\n await this.markReadApiUnsupported(rawUrl, api);\n }\n return release;\n }\n\n override postprocessRelease(\n config: PostprocessReleaseConfig,\n release: Release,\n ): Promise<PostprocessReleaseResult> {\n return withCache(\n {\n namespace: `datasource-crate`,\n key: buildCacheKey(\n 'postprocessRelease',\n config.registryUrl,\n config.packageName,\n release.version,\n ),\n ttlMinutes: 7 * 24 * 60,\n public: CrateDatasource.isCratesIo(config.registryUrl ?? undefined),\n },\n () => this._postprocessRelease(config, release),\n );\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"datasource.js","names":["HttpError"],"sources":["../../../lib/modules/datasource/datasource.ts"],"sourcesContent":["import type { ZodType, z } from 'zod/v4';\nimport { ZodError } from 'zod/v4';\nimport { logger } from '../../logger/index.ts';\nimport { ExternalHostError } from '../../types/errors/external-host-error.ts';\nimport type { PackageCacheNamespace } from '../../util/cache/package/types.ts';\nimport type { CachedOptions } from '../../util/cache/package/with-cache.ts';\nimport { withCache } from '../../util/cache/package/with-cache.ts';\nimport type { HttpBase } from '../../util/http/http.ts';\nimport { Http, HttpError } from '../../util/http/index.ts';\nimport type { HttpOptions } from '../../util/http/types.ts';\nimport { coerceObject } from '../../util/object.ts';\nimport type {\n DatasourceApi,\n DigestConfig,\n GetReleasesConfig,\n PostprocessReleaseConfig,\n PostprocessReleaseResult,\n RegistryStrategy,\n Release,\n ReleaseResult,\n SourceUrlSupport,\n} from './types.ts';\n\n/**\n * The options accepted by the JSON methods of the http client `H`:\n * `HttpOptions` for the plain client, and the client's own options type for a\n * specialised one such as `GithubHttp`.\n */\ntype JsonOptions<H extends Http> =\n H extends HttpBase<infer Opts, any> ? Opts : HttpOptions;\n\nexport abstract class Datasource<\n H extends Http = Http,\n> implements DatasourceApi {\n public readonly id: string;\n\n /**\n * The package cache namespace used by {@link Datasource.cached}.\n * Defaults to `datasource-<id>`, which must be registered in\n * `packageCacheNamespaces`.\n */\n protected readonly cacheNamespace: PackageCacheNamespace;\n\n /**\n * A subclass that narrows `H` to a specialised client (`GithubHttp`,\n * `GitlabHttp`, ...) must construct that client itself and pass it as\n * `http`, since this constructor can only build the plain `Http` default.\n */\n protected constructor(id: string, http?: H) {\n this.id = id;\n this.http = http ?? (new Http(id) as H);\n this.cacheNamespace = `datasource-${id}` as PackageCacheNamespace;\n }\n\n caching: boolean | undefined;\n\n defaultConfig: Record<string, unknown> | undefined;\n\n getDefaultRegistryUrls(_packageName: string): string[] | undefined {\n return undefined;\n }\n\n supportsCustomRegistry(_packageName: string): boolean {\n return true;\n }\n\n defaultVersioning?: string | undefined;\n\n registryStrategy: RegistryStrategy = 'first';\n\n releaseTimestampSupport = false;\n releaseTimestampNote?: string | undefined;\n\n sourceUrlSupport: SourceUrlSupport = 'none';\n sourceUrlNote?: string | undefined;\n\n protected readonly http: H;\n\n abstract getReleases(\n getReleasesConfig: GetReleasesConfig,\n ): Promise<ReleaseResult | null>;\n\n /**\n * `newValue` may be `undefined`, for example when only the digest of the\n * current value is being resolved. Implementations must handle that case\n * explicitly, for example by resolving the digest of a default branch or\n * by returning `null`.\n */\n getDigest?(config: DigestConfig, newValue?: string): Promise<string | null>;\n\n postprocessRelease?(\n config: PostprocessReleaseConfig,\n release: Release,\n ): Promise<PostprocessReleaseResult>;\n\n handleHttpErrors(_err: HttpError): void {\n // intentionally empty\n }\n\n /**\n * Caches the result of `fn` in the datasource cache namespace.\n *\n * Same as {@link withCache}, except that `namespace` defaults to\n * {@link Datasource.cacheNamespace}.\n */\n protected cached<T>(\n options: Omit<CachedOptions, 'namespace'> & {\n namespace?: PackageCacheNamespace;\n },\n fn: () => T | Promise<T>,\n ): Promise<T> {\n return withCache(\n { ...options, namespace: options.namespace ?? this.cacheNamespace },\n fn,\n );\n }\n\n /**\n * Requests `url` and returns the response body validated by `schema`.\n *\n * Every error, a response that fails validation included, goes through\n * {@link Datasource.handleGenericErrors} and therefore fails the lookup. Use\n * it when a malformed response must be treated as an error; use\n * {@link Datasource.fetchJsonOrNull} when it should count as \"no data from\n * this registry\" instead.\n */\n protected async fetchJson<Schema extends ZodType<any, any, any>>(\n url: string,\n schema: Schema,\n options?: JsonOptions<H>,\n ): Promise<z.infer<Schema>> {\n const httpOptions = coerceObject(options) as HttpOptions;\n try {\n const { body } = await this.http.getJson(url, httpOptions, schema);\n return body;\n } catch (err) {\n this.handleGenericErrors(err);\n }\n }\n\n /**\n * Same as {@link Datasource.fetchJson}, except that a response which fails\n * schema validation is logged and reported as `null`.\n *\n * Use it when a malformed response means \"no data from this registry\" rather\n * than a failed lookup. Every other error still goes through\n * {@link Datasource.handleGenericErrors}.\n */\n protected async fetchJsonOrNull<Schema extends ZodType<any, any, any>>(\n url: string,\n schema: Schema,\n options?: JsonOptions<H>,\n ): Promise<z.infer<Schema> | null> {\n try {\n return await this.fetchJson(url, schema, options);\n } catch (err) {\n if (err instanceof ZodError) {\n logger.debug(\n { err, datasource: this.id, url },\n 'Ignoring response that failed schema validation',\n );\n return null;\n }\n\n throw err;\n }\n }\n\n protected handleGenericErrors(err: Error): never {\n if (err instanceof ExternalHostError) {\n throw err;\n }\n\n if (err instanceof HttpError) {\n this.handleHttpErrors(err);\n\n const statusCode = err.response?.statusCode;\n if (\n statusCode &&\n (statusCode === 429 || (statusCode >= 500 && statusCode < 600))\n ) {\n throw new ExternalHostError(err);\n }\n }\n\n throw err;\n }\n}\n"],"mappings":";;;;;;;;AA+BA,IAAsB,aAAtB,MAE2B;CACzB;;;;;;CAOA;;;;;;CAOA,YAAsB,IAAY,MAAU;EAC1C,KAAK,KAAK;EACV,KAAK,OAAO,QAAS,IAAI,KAAK,EAAE;EAChC,KAAK,iBAAiB,cAAc;CACtC;CAEA;CAEA;CAEA,uBAAuB,cAA4C,CAEnE;CAEA,uBAAuB,cAA+B;EACpD,OAAO;CACT;CAEA;CAEA,mBAAqC;CAErC,0BAA0B;CAC1B;CAEA,mBAAqC;CACrC;CAEA;CAmBA,iBAAiB,MAAuB,CAExC;;;;;;;CAQA,OACE,SAGA,IACY;EACZ,OAAO,UACL;GAAE,GAAG;GAAS,WAAW,QAAQ,aAAa,KAAK;EAAe,GAClE,EACF;CACF;;;;;;;;;;CAWA,MAAgB,UACd,KACA,QACA,SAC0B;EAC1B,MAAM,cAAc,aAAa,OAAO;EACxC,IAAI;GACF,MAAM,EAAE,SAAS,MAAM,KAAK,KAAK,QAAQ,KAAK,aAAa,MAAM;GACjE,OAAO;EACT,SAAS,KAAK;GACZ,KAAK,oBAAoB,GAAG;EAC9B;CACF;;;;;;;;;CAUA,MAAgB,gBACd,KACA,QACA,SACiC;EACjC,IAAI;GACF,OAAO,MAAM,KAAK,UAAU,KAAK,QAAQ,OAAO;EAClD,SAAS,KAAK;GACZ,IAAI,eAAe,UAAU;IAC3B,OAAO,MACL;KAAE;KAAK,YAAY,KAAK;KAAI;IAAI,GAChC,iDACF;IACA,OAAO;GACT;GAEA,MAAM;EACR;CACF;CAEA,oBAA8B,KAAmB;EAC/C,IAAI,eAAe,mBACjB,MAAM;EAGR,IAAI,eAAeA,cAAW;GAC5B,KAAK,iBAAiB,GAAG;GAEzB,MAAM,aAAa,IAAI,UAAU;GACjC,IACE,eACC,eAAe,OAAQ,cAAc,OAAO,aAAa,MAE1D,MAAM,IAAI,kBAAkB,GAAG;EAEnC;EAEA,MAAM;CACR;AACF"}
1
+ {"version":3,"file":"datasource.js","names":["HttpError"],"sources":["../../../lib/modules/datasource/datasource.ts"],"sourcesContent":["import type { ZodType, z } from 'zod/v4';\nimport { ZodError } from 'zod/v4';\nimport { logger } from '../../logger/index.ts';\nimport { ExternalHostError } from '../../types/errors/external-host-error.ts';\nimport type { PackageCacheNamespace } from '../../util/cache/package/types.ts';\nimport type {\n CacheContext,\n CachedOptions,\n} from '../../util/cache/package/with-cache.ts';\nimport { withCache } from '../../util/cache/package/with-cache.ts';\nimport type { HttpBase } from '../../util/http/http.ts';\nimport { Http, HttpError } from '../../util/http/index.ts';\nimport type { HttpOptions } from '../../util/http/types.ts';\nimport { coerceObject } from '../../util/object.ts';\nimport type {\n DatasourceApi,\n DigestConfig,\n GetReleasesConfig,\n PostprocessReleaseConfig,\n PostprocessReleaseResult,\n RegistryStrategy,\n Release,\n ReleaseResult,\n SourceUrlSupport,\n} from './types.ts';\n\n/**\n * The options accepted by the JSON methods of the http client `H`:\n * `HttpOptions` for the plain client, and the client's own options type for a\n * specialised one such as `GithubHttp`.\n */\ntype JsonOptions<H extends Http> =\n H extends HttpBase<infer Opts, any> ? Opts : HttpOptions;\n\nexport abstract class Datasource<\n H extends Http = Http,\n> implements DatasourceApi {\n public readonly id: string;\n\n /**\n * The package cache namespace used by {@link Datasource.cached}.\n * Defaults to `datasource-<id>`, which must be registered in\n * `packageCacheNamespaces`.\n */\n protected readonly cacheNamespace: PackageCacheNamespace;\n\n /**\n * A subclass that narrows `H` to a specialised client (`GithubHttp`,\n * `GitlabHttp`, ...) must construct that client itself and pass it as\n * `http`, since this constructor can only build the plain `Http` default.\n */\n protected constructor(id: string, http?: H) {\n this.id = id;\n this.http = http ?? (new Http(id) as H);\n this.cacheNamespace = `datasource-${id}` as PackageCacheNamespace;\n }\n\n caching: boolean | undefined;\n\n defaultConfig: Record<string, unknown> | undefined;\n\n getDefaultRegistryUrls(_packageName: string): string[] | undefined {\n return undefined;\n }\n\n supportsCustomRegistry(_packageName: string): boolean {\n return true;\n }\n\n defaultVersioning?: string | undefined;\n\n registryStrategy: RegistryStrategy = 'first';\n\n releaseTimestampSupport = false;\n releaseTimestampNote?: string | undefined;\n\n sourceUrlSupport: SourceUrlSupport = 'none';\n sourceUrlNote?: string | undefined;\n\n protected readonly http: H;\n\n abstract getReleases(\n getReleasesConfig: GetReleasesConfig,\n ): Promise<ReleaseResult | null>;\n\n /**\n * `newValue` may be `undefined`, for example when only the digest of the\n * current value is being resolved. Implementations must handle that case\n * explicitly, for example by resolving the digest of a default branch or\n * by returning `null`.\n */\n getDigest?(config: DigestConfig, newValue?: string): Promise<string | null>;\n\n postprocessRelease?(\n config: PostprocessReleaseConfig,\n release: Release,\n ): Promise<PostprocessReleaseResult>;\n\n handleHttpErrors(_err: HttpError): void {\n // intentionally empty\n }\n\n /**\n * Caches the result of `fn` in the datasource cache namespace.\n *\n * Same as {@link withCache}, except that `namespace` defaults to\n * {@link Datasource.cacheNamespace}.\n */\n protected cached<T>(\n options: Omit<CachedOptions, 'namespace'> & {\n namespace?: PackageCacheNamespace;\n },\n fn: (ctx: CacheContext) => T | PromiseLike<T>,\n ): Promise<Awaited<T>> {\n return withCache(\n { ...options, namespace: options.namespace ?? this.cacheNamespace },\n fn,\n );\n }\n\n /**\n * Requests `url` and returns the response body validated by `schema`.\n *\n * Every error, a response that fails validation included, goes through\n * {@link Datasource.handleGenericErrors} and therefore fails the lookup. Use\n * it when a malformed response must be treated as an error; use\n * {@link Datasource.fetchJsonOrNull} when it should count as \"no data from\n * this registry\" instead.\n */\n protected async fetchJson<Schema extends ZodType<any, any, any>>(\n url: string,\n schema: Schema,\n options?: JsonOptions<H>,\n ): Promise<z.infer<Schema>> {\n const httpOptions = coerceObject(options) as HttpOptions;\n try {\n const { body } = await this.http.getJson(url, httpOptions, schema);\n return body;\n } catch (err) {\n this.handleGenericErrors(err);\n }\n }\n\n /**\n * Same as {@link Datasource.fetchJson}, except that a response which fails\n * schema validation is logged and reported as `null`.\n *\n * Use it when a malformed response means \"no data from this registry\" rather\n * than a failed lookup. Every other error still goes through\n * {@link Datasource.handleGenericErrors}.\n */\n protected async fetchJsonOrNull<Schema extends ZodType<any, any, any>>(\n url: string,\n schema: Schema,\n options?: JsonOptions<H>,\n ): Promise<z.infer<Schema> | null> {\n try {\n return await this.fetchJson(url, schema, options);\n } catch (err) {\n if (err instanceof ZodError) {\n logger.debug(\n { err, datasource: this.id, url },\n 'Ignoring response that failed schema validation',\n );\n return null;\n }\n\n throw err;\n }\n }\n\n protected handleGenericErrors(err: Error): never {\n if (err instanceof ExternalHostError) {\n throw err;\n }\n\n if (err instanceof HttpError) {\n this.handleHttpErrors(err);\n\n const statusCode = err.response?.statusCode;\n if (\n statusCode &&\n (statusCode === 429 || (statusCode >= 500 && statusCode < 600))\n ) {\n throw new ExternalHostError(err);\n }\n }\n\n throw err;\n }\n}\n"],"mappings":";;;;;;;;AAkCA,IAAsB,aAAtB,MAE2B;CACzB;;;;;;CAOA;;;;;;CAOA,YAAsB,IAAY,MAAU;EAC1C,KAAK,KAAK;EACV,KAAK,OAAO,QAAS,IAAI,KAAK,EAAE;EAChC,KAAK,iBAAiB,cAAc;CACtC;CAEA;CAEA;CAEA,uBAAuB,cAA4C,CAEnE;CAEA,uBAAuB,cAA+B;EACpD,OAAO;CACT;CAEA;CAEA,mBAAqC;CAErC,0BAA0B;CAC1B;CAEA,mBAAqC;CACrC;CAEA;CAmBA,iBAAiB,MAAuB,CAExC;;;;;;;CAQA,OACE,SAGA,IACqB;EACrB,OAAO,UACL;GAAE,GAAG;GAAS,WAAW,QAAQ,aAAa,KAAK;EAAe,GAClE,EACF;CACF;;;;;;;;;;CAWA,MAAgB,UACd,KACA,QACA,SAC0B;EAC1B,MAAM,cAAc,aAAa,OAAO;EACxC,IAAI;GACF,MAAM,EAAE,SAAS,MAAM,KAAK,KAAK,QAAQ,KAAK,aAAa,MAAM;GACjE,OAAO;EACT,SAAS,KAAK;GACZ,KAAK,oBAAoB,GAAG;EAC9B;CACF;;;;;;;;;CAUA,MAAgB,gBACd,KACA,QACA,SACiC;EACjC,IAAI;GACF,OAAO,MAAM,KAAK,UAAU,KAAK,QAAQ,OAAO;EAClD,SAAS,KAAK;GACZ,IAAI,eAAe,UAAU;IAC3B,OAAO,MACL;KAAE;KAAK,YAAY,KAAK;KAAI;IAAI,GAChC,iDACF;IACA,OAAO;GACT;GAEA,MAAM;EACR;CACF;CAEA,oBAA8B,KAAmB;EAC/C,IAAI,eAAe,mBACjB,MAAM;EAGR,IAAI,eAAeA,cAAW;GAC5B,KAAK,iBAAiB,GAAG;GAEzB,MAAM,aAAa,IAAI,UAAU;GACjC,IACE,eACC,eAAe,OAAQ,cAAc,OAAO,aAAa,MAE1D,MAAM,IAAI,kBAAkB,GAAG;EAEnC;EAEA,MAAM;CACR;AACF"}
@@ -236,9 +236,12 @@ var DockerDatasource = class DockerDatasource extends Datasource {
236
236
  return withCache({
237
237
  namespace: "datasource-docker-architecture",
238
238
  key: buildCacheKey(registryHost, dockerRepository, currentDigest),
239
- ttlMinutes: 40320,
240
- shouldCacheResult: isNonEmptyString
241
- }, () => this._getImageArchitecture(registryHost, dockerRepository, currentDigest));
239
+ ttlMinutes: 40320
240
+ }, async (ctx) => {
241
+ const architecture = await this._getImageArchitecture(registryHost, dockerRepository, currentDigest);
242
+ ctx.cacheable = isNonEmptyString(architecture);
243
+ return architecture;
244
+ });
242
245
  }
243
246
  async _getLabels(registryHost, dockerRepository, tag) {
244
247
  logger.debug(`getLabels(${registryHost}, ${dockerRepository}, ${tag})`);
@@ -493,7 +496,7 @@ var DockerDatasource = class DockerDatasource extends Datasource {
493
496
  return withCache({
494
497
  namespace: "datasource-docker-tags",
495
498
  key: buildCacheKey(registryHost, dockerRepository),
496
- cacheable: registryHost === DOCKER_HUB
499
+ public: registryHost === DOCKER_HUB
497
500
  }, () => this._getTags(registryHost, dockerRepository));
498
501
  }
499
502
  /**
@@ -591,9 +594,12 @@ var DockerDatasource = class DockerDatasource extends Datasource {
591
594
  return withCache({
592
595
  namespace: "datasource-docker-digest",
593
596
  key: buildCacheKey(registryHost, dockerRepository, newTag, config.currentDigest),
594
- fallback: true,
595
- shouldCacheResult: isNonEmptyString
596
- }, () => this._getDigest(config, newValue));
597
+ fallback: true
598
+ }, async (ctx) => {
599
+ const digest = await this._getDigest(config, newValue);
600
+ ctx.cacheable = isNonEmptyString(digest);
601
+ return digest;
602
+ });
597
603
  }
598
604
  async _getDockerHubTags(dockerRepository) {
599
605
  let url = `https://hub.docker.com/v2/repositories/${dockerRepository}/tags?page_size=1000&ordering=last_updated`;
@@ -678,7 +684,7 @@ var DockerDatasource = class DockerDatasource extends Datasource {
678
684
  return withCache({
679
685
  namespace: "datasource-docker-releases-v2",
680
686
  key: buildCacheKey(registryHost, dockerRepository),
681
- cacheable: registryHost === DOCKER_HUB,
687
+ public: registryHost === DOCKER_HUB,
682
688
  fallback: true
683
689
  }, () => this._getReleases(config));
684
690
  }