renovate 44.149.1 → 44.149.3

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 (163) hide show
  1. package/dist/config/options/index.js +1 -1
  2. package/dist/config/options/index.js.map +1 -1
  3. package/dist/config/presets/index.js +2 -1
  4. package/dist/config/presets/index.js.map +1 -1
  5. package/dist/global-config-option-defaults.generated.js +1 -1
  6. package/dist/global-config-option-defaults.generated.js.map +1 -1
  7. package/dist/modules/datasource/artifactory/index.js +2 -1
  8. package/dist/modules/datasource/artifactory/index.js.map +1 -1
  9. package/dist/modules/datasource/aws-eks-addon/index.js +6 -5
  10. package/dist/modules/datasource/aws-eks-addon/index.js.map +1 -1
  11. package/dist/modules/datasource/aws-machine-image/index.js +4 -3
  12. package/dist/modules/datasource/aws-machine-image/index.js.map +1 -1
  13. package/dist/modules/datasource/aws-rds/index.js +2 -1
  14. package/dist/modules/datasource/aws-rds/index.js.map +1 -1
  15. package/dist/modules/datasource/azure-bicep-resource/index.js +2 -1
  16. package/dist/modules/datasource/azure-bicep-resource/index.js.map +1 -1
  17. package/dist/modules/datasource/bazel/index.js +2 -1
  18. package/dist/modules/datasource/bazel/index.js.map +1 -1
  19. package/dist/modules/datasource/bitrise/index.js +2 -1
  20. package/dist/modules/datasource/bitrise/index.js.map +1 -1
  21. package/dist/modules/datasource/buildpacks-registry/index.js +2 -1
  22. package/dist/modules/datasource/buildpacks-registry/index.js.map +1 -1
  23. package/dist/modules/datasource/cdnjs/index.js +3 -2
  24. package/dist/modules/datasource/cdnjs/index.js.map +1 -1
  25. package/dist/modules/datasource/conan/index.js +3 -2
  26. package/dist/modules/datasource/conan/index.js.map +1 -1
  27. package/dist/modules/datasource/conda/index.js +2 -1
  28. package/dist/modules/datasource/conda/index.js.map +1 -1
  29. package/dist/modules/datasource/cpan/index.js +1 -1
  30. package/dist/modules/datasource/cpan/index.js.map +1 -1
  31. package/dist/modules/datasource/crate/index.js +17 -9
  32. package/dist/modules/datasource/crate/index.js.map +1 -1
  33. package/dist/modules/datasource/deb/index.js +3 -2
  34. package/dist/modules/datasource/deb/index.js.map +1 -1
  35. package/dist/modules/datasource/deno/index.js +4 -3
  36. package/dist/modules/datasource/deno/index.js.map +1 -1
  37. package/dist/modules/datasource/docker/index.js +9 -9
  38. package/dist/modules/datasource/docker/index.js.map +1 -1
  39. package/dist/modules/datasource/dotnet-version/index.js +2 -1
  40. package/dist/modules/datasource/dotnet-version/index.js.map +1 -1
  41. package/dist/modules/datasource/elm-package/index.js +2 -1
  42. package/dist/modules/datasource/elm-package/index.js.map +1 -1
  43. package/dist/modules/datasource/endoflife-date/index.js +2 -1
  44. package/dist/modules/datasource/endoflife-date/index.js.map +1 -1
  45. package/dist/modules/datasource/galaxy-collection/index.js +3 -2
  46. package/dist/modules/datasource/galaxy-collection/index.js.map +1 -1
  47. package/dist/modules/datasource/git-host-tags.js +6 -5
  48. package/dist/modules/datasource/git-host-tags.js.map +1 -1
  49. package/dist/modules/datasource/gitea-tags/base.js +5 -4
  50. package/dist/modules/datasource/gitea-tags/base.js.map +1 -1
  51. package/dist/modules/datasource/gitea-tags/util.js +2 -5
  52. package/dist/modules/datasource/gitea-tags/util.js.map +1 -1
  53. package/dist/modules/datasource/github-digest/index.js +3 -5
  54. package/dist/modules/datasource/github-digest/index.js.map +1 -1
  55. package/dist/modules/datasource/github-release-attachments/index.js +3 -2
  56. package/dist/modules/datasource/github-release-attachments/index.js.map +1 -1
  57. package/dist/modules/datasource/gitlab-packages/index.js +2 -1
  58. package/dist/modules/datasource/gitlab-packages/index.js.map +1 -1
  59. package/dist/modules/datasource/gitlab-releases/index.js +2 -1
  60. package/dist/modules/datasource/gitlab-releases/index.js.map +1 -1
  61. package/dist/modules/datasource/gitlab-tags/index.js +3 -2
  62. package/dist/modules/datasource/gitlab-tags/index.js.map +1 -1
  63. package/dist/modules/datasource/glasskube-packages/index.js +2 -1
  64. package/dist/modules/datasource/glasskube-packages/index.js.map +1 -1
  65. package/dist/modules/datasource/go/goproxy-parser.js +3 -2
  66. package/dist/modules/datasource/go/goproxy-parser.js.map +1 -1
  67. package/dist/modules/datasource/go/index.js +4 -3
  68. package/dist/modules/datasource/go/index.js.map +1 -1
  69. package/dist/modules/datasource/go/releases-goproxy.js +6 -3
  70. package/dist/modules/datasource/go/releases-goproxy.js.map +1 -1
  71. package/dist/modules/datasource/go/timestamp-cache.js +3 -1
  72. package/dist/modules/datasource/go/timestamp-cache.js.map +1 -1
  73. package/dist/modules/datasource/golang-version/index.js +1 -1
  74. package/dist/modules/datasource/golang-version/index.js.map +1 -1
  75. package/dist/modules/datasource/gradle-version/index.js +1 -1
  76. package/dist/modules/datasource/gradle-version/index.js.map +1 -1
  77. package/dist/modules/datasource/helm/index.js +2 -1
  78. package/dist/modules/datasource/helm/index.js.map +1 -1
  79. package/dist/modules/datasource/hermit/index.js +3 -2
  80. package/dist/modules/datasource/hermit/index.js.map +1 -1
  81. package/dist/modules/datasource/hex/index.js +3 -2
  82. package/dist/modules/datasource/hex/index.js.map +1 -1
  83. package/dist/modules/datasource/hexpm-bob/index.js +2 -1
  84. package/dist/modules/datasource/hexpm-bob/index.js.map +1 -1
  85. package/dist/modules/datasource/index.js +4 -3
  86. package/dist/modules/datasource/index.js.map +1 -1
  87. package/dist/modules/datasource/java-version/index.js +2 -1
  88. package/dist/modules/datasource/java-version/index.js.map +1 -1
  89. package/dist/modules/datasource/jsr/index.js +2 -1
  90. package/dist/modules/datasource/jsr/index.js.map +1 -1
  91. package/dist/modules/datasource/maven/index.js +2 -1
  92. package/dist/modules/datasource/maven/index.js.map +1 -1
  93. package/dist/modules/datasource/nextcloud/index.js +2 -1
  94. package/dist/modules/datasource/nextcloud/index.js.map +1 -1
  95. package/dist/modules/datasource/node-version/index.js +1 -1
  96. package/dist/modules/datasource/node-version/index.js.map +1 -1
  97. package/dist/modules/datasource/nuget/v3.js +3 -2
  98. package/dist/modules/datasource/nuget/v3.js.map +1 -1
  99. package/dist/modules/datasource/orb/index.js +2 -1
  100. package/dist/modules/datasource/orb/index.js.map +1 -1
  101. package/dist/modules/datasource/packagist/index.js +4 -3
  102. package/dist/modules/datasource/packagist/index.js.map +1 -1
  103. package/dist/modules/datasource/pod/index.js +2 -1
  104. package/dist/modules/datasource/pod/index.js.map +1 -1
  105. package/dist/modules/datasource/python-version/index.js +1 -1
  106. package/dist/modules/datasource/python-version/index.js.map +1 -1
  107. package/dist/modules/datasource/repology/index.js +2 -1
  108. package/dist/modules/datasource/repology/index.js.map +1 -1
  109. package/dist/modules/datasource/rpm/index.js +2 -1
  110. package/dist/modules/datasource/rpm/index.js.map +1 -1
  111. package/dist/modules/datasource/rubygems/index.js +2 -1
  112. package/dist/modules/datasource/rubygems/index.js.map +1 -1
  113. package/dist/modules/datasource/rubygems/metadata-cache.js +2 -1
  114. package/dist/modules/datasource/rubygems/metadata-cache.js.map +1 -1
  115. package/dist/modules/datasource/sbt-package/index.js +11 -10
  116. package/dist/modules/datasource/sbt-package/index.js.map +1 -1
  117. package/dist/modules/datasource/terraform-module/index.js +2 -1
  118. package/dist/modules/datasource/terraform-module/index.js.map +1 -1
  119. package/dist/modules/datasource/terraform-provider/index.js +6 -5
  120. package/dist/modules/datasource/terraform-provider/index.js.map +1 -1
  121. package/dist/modules/datasource/unity3d/index.js +2 -1
  122. package/dist/modules/datasource/unity3d/index.js.map +1 -1
  123. package/dist/modules/datasource/unity3d-packages/index.js +2 -1
  124. package/dist/modules/datasource/unity3d-packages/index.js.map +1 -1
  125. package/dist/modules/manager/bazel/parser.js +3 -1
  126. package/dist/modules/manager/bazel/parser.js.map +1 -1
  127. package/dist/modules/manager/github-actions/extract.js +2 -1
  128. package/dist/modules/manager/github-actions/extract.js.map +1 -1
  129. package/dist/modules/manager/terraform/lockfile/hash.js +2 -1
  130. package/dist/modules/manager/terraform/lockfile/hash.js.map +1 -1
  131. package/dist/modules/platform/bitbucket/pr-cache.js +3 -2
  132. package/dist/modules/platform/bitbucket/pr-cache.js.map +1 -1
  133. package/dist/modules/platform/bitbucket-server/pr-cache.js +3 -2
  134. package/dist/modules/platform/bitbucket-server/pr-cache.js.map +1 -1
  135. package/dist/modules/platform/gitea/pr-cache.js +7 -6
  136. package/dist/modules/platform/gitea/pr-cache.js.map +1 -1
  137. package/dist/modules/platform/gitlab/pr-cache.js +3 -2
  138. package/dist/modules/platform/gitlab/pr-cache.js.map +1 -1
  139. package/dist/util/cache/package/index.js +1 -1
  140. package/dist/util/cache/package/key.js +8 -1
  141. package/dist/util/cache/package/key.js.map +1 -1
  142. package/dist/util/cache/package/with-cache.js +2 -1
  143. package/dist/util/cache/package/with-cache.js.map +1 -1
  144. package/dist/util/exec/containerbase.js +2 -1
  145. package/dist/util/exec/containerbase.js.map +1 -1
  146. package/dist/util/github/graphql/cache-strategies/memory-cache-strategy.js +2 -1
  147. package/dist/util/github/graphql/cache-strategies/memory-cache-strategy.js.map +1 -1
  148. package/dist/util/github/graphql/datasource-fetcher.js +5 -8
  149. package/dist/util/github/graphql/datasource-fetcher.js.map +1 -1
  150. package/dist/util/http/cache/memory-http-cache-provider.js +2 -1
  151. package/dist/util/http/cache/memory-http-cache-provider.js.map +1 -1
  152. package/dist/util/http/cache/package-http-cache-provider.js +2 -1
  153. package/dist/util/http/cache/package-http-cache-provider.js.map +1 -1
  154. package/dist/util/jsonata.js +2 -1
  155. package/dist/util/jsonata.js.map +1 -1
  156. package/dist/util/merge-confidence/index.js +10 -7
  157. package/dist/util/merge-confidence/index.js.map +1 -1
  158. package/dist/workers/repository/update/pr/changelog/release-notes.js +4 -5
  159. package/dist/workers/repository/update/pr/changelog/release-notes.js.map +1 -1
  160. package/dist/workers/repository/update/pr/changelog/source.js +2 -1
  161. package/dist/workers/repository/update/pr/changelog/source.js.map +1 -1
  162. package/package.json +2 -2
  163. package/renovate-schema.json +2 -2
@@ -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 { 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: `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: `getDigest:${config.registryUrl}:${config.packageName}:${newValue}`,\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":";;;;;;AAaA,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,eAAe;GACpB,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,aAAa,OAAO,YAAY,GAAG,OAAO,YAAY,GAAG;GAC9D,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 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,6 +1,7 @@
1
1
  import { regEx } from "../../../util/regex.js";
2
2
  import { logger } from "../../../logger/index.js";
3
3
  import { ensureTrailingSlash, joinUrlParts } from "../../../util/url.js";
4
+ import { buildCacheKey } from "../../../util/cache/package/key.js";
4
5
  import { get } from "../../versioning/index.js";
5
6
  import { Datasource } from "../datasource.js";
6
7
  import { GithubHttp } from "../../../util/http/github.js";
@@ -44,7 +45,7 @@ var ConanDatasource = class ConanDatasource extends Datasource {
44
45
  }
45
46
  getDigest(config, newValue) {
46
47
  return this.cached({
47
- key: `getDigest:${config.registryUrl}:${config.packageName}:${newValue}`,
48
+ key: buildCacheKey("getDigest", config.registryUrl, config.packageName, newValue),
48
49
  fallback: true
49
50
  }, () => this.fetchDigest(config, newValue));
50
51
  }
@@ -98,7 +99,7 @@ var ConanDatasource = class ConanDatasource extends Datasource {
98
99
  }
99
100
  getReleases(config) {
100
101
  return this.cached({
101
- key: `getReleases:${config.registryUrl}:${config.packageName}`,
102
+ key: buildCacheKey("getReleases", config.registryUrl, config.packageName),
102
103
  fallback: true
103
104
  }, () => this.fetchReleases(config));
104
105
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["allVersioning.get"],"sources":["../../../../lib/modules/datasource/conan/index.ts"],"sourcesContent":["import { isString, isUndefined } from '@sindresorhus/is';\nimport { logger } from '../../../logger/index.ts';\nimport { GithubHttp } from '../../../util/http/github.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { ensureTrailingSlash, joinUrlParts } from '../../../util/url.ts';\nimport * as allVersioning from '../../versioning/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type {\n DigestConfig,\n GetReleasesConfig,\n Release,\n ReleaseResult,\n} from '../types.ts';\nimport { isArtifactoryServer } from '../util.ts';\nimport { datasource, defaultRegistryUrl, getConanPackage } from './common.ts';\nimport {\n ConanCenterReleases,\n ConanJSON,\n ConanLatestRevision,\n ConanProperties,\n ConanRevisionJSON,\n} from './schema.ts';\n\nexport class ConanDatasource extends Datasource {\n static readonly id = datasource;\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return [defaultRegistryUrl];\n }\n\n override readonly registryStrategy = 'merge';\n\n githubHttp: GithubHttp;\n\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'The source URL is supported only if the package is served from the Artifactory servers. In which case we determine it from the `properties[conan.package.url]` field in the results.';\n\n constructor(id = ConanDatasource.id) {\n super(id);\n this.githubHttp = new GithubHttp(id);\n }\n\n async getConanCenterReleases(\n conanName: string,\n userAndChannel: string,\n ): Promise<ReleaseResult | null> {\n if (userAndChannel && userAndChannel !== '@_/_') {\n logger.debug(\n { conanName, userAndChannel },\n 'User/channel not supported for Conan Center lookups',\n );\n return null;\n }\n const url = `https://api.github.com/repos/conan-io/conan-center-index/contents/recipes/${conanName}/config.yml`;\n const { body: result } = await this.githubHttp.getYaml(\n url,\n { headers: { accept: 'application/vnd.github.v3.raw' } },\n ConanCenterReleases,\n );\n return result;\n }\n\n private async fetchDigest(\n { registryUrl, packageName }: DigestConfig,\n newValue?: string,\n ): Promise<string | null> {\n if (isUndefined(newValue) || isUndefined(registryUrl)) {\n return null;\n }\n const url = ensureTrailingSlash(registryUrl);\n const conanPackage = getConanPackage(packageName);\n const revisionLookUp = joinUrlParts(\n url,\n 'v2/conans/',\n conanPackage.conanName,\n newValue,\n conanPackage.userAndChannel,\n '/revisions',\n );\n const { body: digest } = await this.http.getJson(\n revisionLookUp,\n ConanLatestRevision,\n );\n return digest;\n }\n\n override getDigest(\n config: DigestConfig,\n newValue?: string,\n ): Promise<string | null> {\n return this.cached(\n {\n // TODO: types (#22198)\n key: `getDigest:${config.registryUrl!}:${config.packageName}:${newValue!}`,\n fallback: true,\n },\n () => this.fetchDigest(config, newValue),\n );\n }\n\n private async fetchReleases({\n registryUrl,\n packageName,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n const conanPackage = getConanPackage(packageName);\n const userAndChannel = `@${conanPackage.userAndChannel}`;\n if (\n isString(registryUrl) &&\n ensureTrailingSlash(registryUrl) === defaultRegistryUrl\n ) {\n return this.getConanCenterReleases(\n conanPackage.conanName,\n userAndChannel,\n );\n }\n\n logger.trace(\n { packageName, registryUrl },\n 'Looking up conan api dependency',\n );\n\n // v8 ignore else -- the datasource layer always supplies a registry url\n if (registryUrl) {\n const url = ensureTrailingSlash(registryUrl);\n const lookupUrl = joinUrlParts(\n url,\n `v2/conans/search?q=${conanPackage.conanName}`,\n );\n\n try {\n const rep = await this.http.getJson(lookupUrl, ConanJSON);\n const conanJson = rep.body;\n if (conanJson) {\n logger.trace({ lookupUrl }, 'Got conan api result');\n const dep: ReleaseResult = { releases: [] };\n\n const conanJsonReleases: Release[] = conanJson\n .filter(({ userChannel }) => userChannel === userAndChannel)\n .map(({ version }) => ({ version }));\n dep.releases.push(...conanJsonReleases);\n\n try {\n if (isArtifactoryServer(rep)) {\n const conanApiRegexp = regEx(\n /(?<host>.*)\\/artifactory\\/api\\/conan\\/(?<repo>[^/]+)/,\n );\n const groups = conanApiRegexp.exec(url)?.groups;\n if (!groups) {\n return dep;\n }\n const semver = allVersioning.get('semver');\n\n const sortedReleases = dep.releases\n .filter((release) => semver.isVersion(release.version))\n .sort((a, b) => semver.sortVersions(a.version, b.version));\n\n const latestVersion = sortedReleases.at(-1)?.version;\n\n if (!latestVersion) {\n return dep;\n }\n logger.debug(\n `Conan package ${packageName} has latest version ${latestVersion}`,\n );\n\n const latestRevisionUrl = joinUrlParts(\n url,\n `v2/conans/${conanPackage.conanName}/${latestVersion}/${conanPackage.userAndChannel}/latest`,\n );\n const {\n body: { revision: packageRev },\n } = await this.http.getJson(latestRevisionUrl, ConanRevisionJSON);\n\n const [user, channel] = conanPackage.userAndChannel.split('/');\n const packageUrl = joinUrlParts(\n `${groups.host}/artifactory/api/storage/${groups.repo}`,\n `${user}/${conanPackage.conanName}/${latestVersion}/${channel}/${packageRev}/export/conanfile.py?properties=conan.package.url`,\n );\n const { body: conanProperties } = await this.http.getJson(\n packageUrl,\n ConanProperties,\n );\n const { sourceUrl } = conanProperties;\n // v8 ignore else -- needs a full artifactory properties fixture\n if (sourceUrl) {\n dep.sourceUrl = sourceUrl;\n }\n }\n } catch (err) {\n logger.debug({ err }, \"Couldn't determine Conan package url\");\n }\n return dep;\n }\n } catch (err) {\n this.handleGenericErrors(err);\n }\n }\n\n return null;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return this.cached(\n {\n // TODO: types (#22198)\n key: `getReleases:${config.registryUrl}:${config.packageName}`,\n fallback: true,\n },\n () => this.fetchReleases(config),\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["allVersioning.get"],"sources":["../../../../lib/modules/datasource/conan/index.ts"],"sourcesContent":["import { isString, isUndefined } from '@sindresorhus/is';\nimport { logger } from '../../../logger/index.ts';\nimport { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { GithubHttp } from '../../../util/http/github.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { ensureTrailingSlash, joinUrlParts } from '../../../util/url.ts';\nimport * as allVersioning from '../../versioning/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type {\n DigestConfig,\n GetReleasesConfig,\n Release,\n ReleaseResult,\n} from '../types.ts';\nimport { isArtifactoryServer } from '../util.ts';\nimport { datasource, defaultRegistryUrl, getConanPackage } from './common.ts';\nimport {\n ConanCenterReleases,\n ConanJSON,\n ConanLatestRevision,\n ConanProperties,\n ConanRevisionJSON,\n} from './schema.ts';\n\nexport class ConanDatasource extends Datasource {\n static readonly id = datasource;\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return [defaultRegistryUrl];\n }\n\n override readonly registryStrategy = 'merge';\n\n githubHttp: GithubHttp;\n\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'The source URL is supported only if the package is served from the Artifactory servers. In which case we determine it from the `properties[conan.package.url]` field in the results.';\n\n constructor(id = ConanDatasource.id) {\n super(id);\n this.githubHttp = new GithubHttp(id);\n }\n\n async getConanCenterReleases(\n conanName: string,\n userAndChannel: string,\n ): Promise<ReleaseResult | null> {\n if (userAndChannel && userAndChannel !== '@_/_') {\n logger.debug(\n { conanName, userAndChannel },\n 'User/channel not supported for Conan Center lookups',\n );\n return null;\n }\n const url = `https://api.github.com/repos/conan-io/conan-center-index/contents/recipes/${conanName}/config.yml`;\n const { body: result } = await this.githubHttp.getYaml(\n url,\n { headers: { accept: 'application/vnd.github.v3.raw' } },\n ConanCenterReleases,\n );\n return result;\n }\n\n private async fetchDigest(\n { registryUrl, packageName }: DigestConfig,\n newValue?: string,\n ): Promise<string | null> {\n if (isUndefined(newValue) || isUndefined(registryUrl)) {\n return null;\n }\n const url = ensureTrailingSlash(registryUrl);\n const conanPackage = getConanPackage(packageName);\n const revisionLookUp = joinUrlParts(\n url,\n 'v2/conans/',\n conanPackage.conanName,\n newValue,\n conanPackage.userAndChannel,\n '/revisions',\n );\n const { body: digest } = await this.http.getJson(\n revisionLookUp,\n ConanLatestRevision,\n );\n return digest;\n }\n\n override getDigest(\n config: DigestConfig,\n newValue?: string,\n ): Promise<string | null> {\n return this.cached(\n {\n key: buildCacheKey(\n 'getDigest',\n config.registryUrl,\n config.packageName,\n newValue,\n ),\n fallback: true,\n },\n () => this.fetchDigest(config, newValue),\n );\n }\n\n private async fetchReleases({\n registryUrl,\n packageName,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n const conanPackage = getConanPackage(packageName);\n const userAndChannel = `@${conanPackage.userAndChannel}`;\n if (\n isString(registryUrl) &&\n ensureTrailingSlash(registryUrl) === defaultRegistryUrl\n ) {\n return this.getConanCenterReleases(\n conanPackage.conanName,\n userAndChannel,\n );\n }\n\n logger.trace(\n { packageName, registryUrl },\n 'Looking up conan api dependency',\n );\n\n // v8 ignore else -- the datasource layer always supplies a registry url\n if (registryUrl) {\n const url = ensureTrailingSlash(registryUrl);\n const lookupUrl = joinUrlParts(\n url,\n `v2/conans/search?q=${conanPackage.conanName}`,\n );\n\n try {\n const rep = await this.http.getJson(lookupUrl, ConanJSON);\n const conanJson = rep.body;\n if (conanJson) {\n logger.trace({ lookupUrl }, 'Got conan api result');\n const dep: ReleaseResult = { releases: [] };\n\n const conanJsonReleases: Release[] = conanJson\n .filter(({ userChannel }) => userChannel === userAndChannel)\n .map(({ version }) => ({ version }));\n dep.releases.push(...conanJsonReleases);\n\n try {\n if (isArtifactoryServer(rep)) {\n const conanApiRegexp = regEx(\n /(?<host>.*)\\/artifactory\\/api\\/conan\\/(?<repo>[^/]+)/,\n );\n const groups = conanApiRegexp.exec(url)?.groups;\n if (!groups) {\n return dep;\n }\n const semver = allVersioning.get('semver');\n\n const sortedReleases = dep.releases\n .filter((release) => semver.isVersion(release.version))\n .sort((a, b) => semver.sortVersions(a.version, b.version));\n\n const latestVersion = sortedReleases.at(-1)?.version;\n\n if (!latestVersion) {\n return dep;\n }\n logger.debug(\n `Conan package ${packageName} has latest version ${latestVersion}`,\n );\n\n const latestRevisionUrl = joinUrlParts(\n url,\n `v2/conans/${conanPackage.conanName}/${latestVersion}/${conanPackage.userAndChannel}/latest`,\n );\n const {\n body: { revision: packageRev },\n } = await this.http.getJson(latestRevisionUrl, ConanRevisionJSON);\n\n const [user, channel] = conanPackage.userAndChannel.split('/');\n const packageUrl = joinUrlParts(\n `${groups.host}/artifactory/api/storage/${groups.repo}`,\n `${user}/${conanPackage.conanName}/${latestVersion}/${channel}/${packageRev}/export/conanfile.py?properties=conan.package.url`,\n );\n const { body: conanProperties } = await this.http.getJson(\n packageUrl,\n ConanProperties,\n );\n const { sourceUrl } = conanProperties;\n // v8 ignore else -- needs a full artifactory properties fixture\n if (sourceUrl) {\n dep.sourceUrl = sourceUrl;\n }\n }\n } catch (err) {\n logger.debug({ err }, \"Couldn't determine Conan package url\");\n }\n return dep;\n }\n } catch (err) {\n this.handleGenericErrors(err);\n }\n }\n\n return null;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return this.cached(\n {\n key: buildCacheKey(\n 'getReleases',\n config.registryUrl,\n config.packageName,\n ),\n fallback: true,\n },\n () => this.fetchReleases(config),\n );\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { coerceArray } from "../../../util/array.js";
2
2
  import { logger } from "../../../logger/index.js";
3
3
  import { ensureTrailingSlash, joinUrlParts } from "../../../util/url.js";
4
+ import { buildCacheKey } from "../../../util/cache/package/key.js";
4
5
  import { ExternalHostError } from "../../../types/errors/external-host-error.js";
5
6
  import { RequestError } from "../../../util/http/got.js";
6
7
  import "../../../util/http/index.js";
@@ -59,7 +60,7 @@ var CondaDatasource = class extends Datasource {
59
60
  }
60
61
  getReleases(config) {
61
62
  return this.cached({
62
- key: `${config.registryUrl}:${config.packageName}`,
63
+ key: buildCacheKey(config.registryUrl, config.packageName),
63
64
  fallback: true
64
65
  }, () => this.fetchReleases(config));
65
66
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["prefixDev.getReleases","HttpError"],"sources":["../../../../lib/modules/datasource/conda/index.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { coerceArray } from '../../../util/array.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport { Timestamp } from '../../../util/timestamp.ts';\nimport { ensureTrailingSlash, joinUrlParts } from '../../../util/url.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, Release, ReleaseResult } from '../types.ts';\nimport { datasource, defaultRegistryUrl, isPrefixDevUrl } from './common.ts';\nimport * as prefixDev from './prefix-dev.ts';\nimport { CondaPackage } from './schema.ts';\n\nexport class CondaDatasource extends Datasource {\n static readonly id = datasource;\n\n constructor() {\n super(datasource);\n }\n\n override supportsCustomRegistry(_packageName: string): boolean {\n return true;\n }\n\n override readonly registryStrategy = 'hunt';\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return [defaultRegistryUrl];\n }\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `upload_time` field of the files of a version when using the Anaconda.org API, or from the `createdAt` field of the variants of a version when using prefix.dev. All files of a version are assumed to be published at roughly the same time.';\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `dev_url` field in the results.';\n\n private async fetchReleases({\n registryUrl,\n packageName,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n logger.trace({ registryUrl, packageName }, 'fetching conda package');\n\n if (!registryUrl) {\n return null;\n }\n\n // We expect registryUrl to be `https://prefix.dev/${channel}` here.\n if (isPrefixDevUrl(registryUrl)) {\n // Since the registryUrl contains at least 3 `/` ,\n // the channel varitable won't be undefined in any case.\n const channel = ensureTrailingSlash(registryUrl).split('/').at(-2)!;\n\n return prefixDev.getReleases(this.http, channel, packageName);\n }\n\n const url = joinUrlParts(registryUrl, packageName);\n\n const result: ReleaseResult = {\n releases: [],\n };\n\n try {\n const response = await this.http.getJson(url, CondaPackage);\n\n result.homepage = response.body.html_url;\n result.sourceUrl = response.body.dev_url;\n\n const releaseDate: Record<string, Timestamp> = {};\n // we assume all packages are roughly released on the same time\n for (const file of coerceArray(response.body.files)) {\n releaseDate[file.version] ??= Timestamp.parse(file.upload_time);\n }\n\n coerceArray(response.body.versions).forEach((version: string) => {\n const thisRelease: Release = {\n version,\n releaseTimestamp: releaseDate[version],\n };\n result.releases.push(thisRelease);\n });\n } catch (err) {\n if (err instanceof HttpError && err.response?.statusCode !== 404) {\n throw new ExternalHostError(err);\n }\n this.handleGenericErrors(err);\n }\n\n return result.releases.length ? result : null;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return this.cached(\n {\n // TODO: types (#22198)\n key: `${config.registryUrl}:${config.packageName}`,\n fallback: true,\n },\n () => this.fetchReleases(config),\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;AAYA,IAAa,kBAAb,cAAqC,WAAW;CAC9C,OAAgB,KAAK;CAErB,cAAc;EACZ,MAAM,UAAU;CAClB;CAEA,uBAAgC,cAA+B;EAC7D,OAAO;CACT;CAEA,mBAAqC;CAErC,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,kBAAkB;CAC5B;CAEA,0BAA4C;CAC5C,uBACE;CACF,mBAAqC;CACrC,gBACE;CAEF,MAAc,cAAc,EAC1B,aACA,eACmD;EACnD,OAAO,MAAM;GAAE;GAAa;EAAY,GAAG,wBAAwB;EAEnE,IAAI,CAAC,aACH,OAAO;EAIT,IAAI,eAAe,WAAW,GAAG;GAG/B,MAAM,UAAU,oBAAoB,WAAW,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE;GAEjE,OAAOA,YAAsB,KAAK,MAAM,SAAS,WAAW;EAC9D;EAEA,MAAM,MAAM,aAAa,aAAa,WAAW;EAEjD,MAAM,SAAwB,EAC5B,UAAU,CAAC,EACb;EAEA,IAAI;GACF,MAAM,WAAW,MAAM,KAAK,KAAK,QAAQ,KAAK,YAAY;GAE1D,OAAO,WAAW,SAAS,KAAK;GAChC,OAAO,YAAY,SAAS,KAAK;GAEjC,MAAM,cAAyC,CAAC;GAEhD,KAAK,MAAM,QAAQ,YAAY,SAAS,KAAK,KAAK,GAChD,YAAY,KAAK,aAAa,UAAU,MAAM,KAAK,WAAW;GAGhE,YAAY,SAAS,KAAK,QAAQ,CAAC,CAAC,SAAS,YAAoB;IAC/D,MAAM,cAAuB;KAC3B;KACA,kBAAkB,YAAY;IAChC;IACA,OAAO,SAAS,KAAK,WAAW;GAClC,CAAC;EACH,SAAS,KAAK;GACZ,IAAI,eAAeC,gBAAa,IAAI,UAAU,eAAe,KAC3D,MAAM,IAAI,kBAAkB,GAAG;GAEjC,KAAK,oBAAoB,GAAG;EAC9B;EAEA,OAAO,OAAO,SAAS,SAAS,SAAS;CAC3C;CAEA,YAAY,QAA0D;EACpE,OAAO,KAAK,OACV;GAEE,KAAK,GAAG,OAAO,YAAY,GAAG,OAAO;GACrC,UAAU;EACZ,SACM,KAAK,cAAc,MAAM,CACjC;CACF;AACF"}
1
+ {"version":3,"file":"index.js","names":["prefixDev.getReleases","HttpError"],"sources":["../../../../lib/modules/datasource/conda/index.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { coerceArray } from '../../../util/array.ts';\nimport { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport { Timestamp } from '../../../util/timestamp.ts';\nimport { ensureTrailingSlash, joinUrlParts } from '../../../util/url.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, Release, ReleaseResult } from '../types.ts';\nimport { datasource, defaultRegistryUrl, isPrefixDevUrl } from './common.ts';\nimport * as prefixDev from './prefix-dev.ts';\nimport { CondaPackage } from './schema.ts';\n\nexport class CondaDatasource extends Datasource {\n static readonly id = datasource;\n\n constructor() {\n super(datasource);\n }\n\n override supportsCustomRegistry(_packageName: string): boolean {\n return true;\n }\n\n override readonly registryStrategy = 'hunt';\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return [defaultRegistryUrl];\n }\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `upload_time` field of the files of a version when using the Anaconda.org API, or from the `createdAt` field of the variants of a version when using prefix.dev. All files of a version are assumed to be published at roughly the same time.';\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `dev_url` field in the results.';\n\n private async fetchReleases({\n registryUrl,\n packageName,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n logger.trace({ registryUrl, packageName }, 'fetching conda package');\n\n if (!registryUrl) {\n return null;\n }\n\n // We expect registryUrl to be `https://prefix.dev/${channel}` here.\n if (isPrefixDevUrl(registryUrl)) {\n // Since the registryUrl contains at least 3 `/` ,\n // the channel varitable won't be undefined in any case.\n const channel = ensureTrailingSlash(registryUrl).split('/').at(-2)!;\n\n return prefixDev.getReleases(this.http, channel, packageName);\n }\n\n const url = joinUrlParts(registryUrl, packageName);\n\n const result: ReleaseResult = {\n releases: [],\n };\n\n try {\n const response = await this.http.getJson(url, CondaPackage);\n\n result.homepage = response.body.html_url;\n result.sourceUrl = response.body.dev_url;\n\n const releaseDate: Record<string, Timestamp> = {};\n // we assume all packages are roughly released on the same time\n for (const file of coerceArray(response.body.files)) {\n releaseDate[file.version] ??= Timestamp.parse(file.upload_time);\n }\n\n coerceArray(response.body.versions).forEach((version: string) => {\n const thisRelease: Release = {\n version,\n releaseTimestamp: releaseDate[version],\n };\n result.releases.push(thisRelease);\n });\n } catch (err) {\n if (err instanceof HttpError && err.response?.statusCode !== 404) {\n throw new ExternalHostError(err);\n }\n this.handleGenericErrors(err);\n }\n\n return result.releases.length ? result : null;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return this.cached(\n {\n key: buildCacheKey(config.registryUrl, config.packageName),\n fallback: true,\n },\n () => this.fetchReleases(config),\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,IAAa,kBAAb,cAAqC,WAAW;CAC9C,OAAgB,KAAK;CAErB,cAAc;EACZ,MAAM,UAAU;CAClB;CAEA,uBAAgC,cAA+B;EAC7D,OAAO;CACT;CAEA,mBAAqC;CAErC,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,kBAAkB;CAC5B;CAEA,0BAA4C;CAC5C,uBACE;CACF,mBAAqC;CACrC,gBACE;CAEF,MAAc,cAAc,EAC1B,aACA,eACmD;EACnD,OAAO,MAAM;GAAE;GAAa;EAAY,GAAG,wBAAwB;EAEnE,IAAI,CAAC,aACH,OAAO;EAIT,IAAI,eAAe,WAAW,GAAG;GAG/B,MAAM,UAAU,oBAAoB,WAAW,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE;GAEjE,OAAOA,YAAsB,KAAK,MAAM,SAAS,WAAW;EAC9D;EAEA,MAAM,MAAM,aAAa,aAAa,WAAW;EAEjD,MAAM,SAAwB,EAC5B,UAAU,CAAC,EACb;EAEA,IAAI;GACF,MAAM,WAAW,MAAM,KAAK,KAAK,QAAQ,KAAK,YAAY;GAE1D,OAAO,WAAW,SAAS,KAAK;GAChC,OAAO,YAAY,SAAS,KAAK;GAEjC,MAAM,cAAyC,CAAC;GAEhD,KAAK,MAAM,QAAQ,YAAY,SAAS,KAAK,KAAK,GAChD,YAAY,KAAK,aAAa,UAAU,MAAM,KAAK,WAAW;GAGhE,YAAY,SAAS,KAAK,QAAQ,CAAC,CAAC,SAAS,YAAoB;IAC/D,MAAM,cAAuB;KAC3B;KACA,kBAAkB,YAAY;IAChC;IACA,OAAO,SAAS,KAAK,WAAW;GAClC,CAAC;EACH,SAAS,KAAK;GACZ,IAAI,eAAeC,gBAAa,IAAI,UAAU,eAAe,KAC3D,MAAM,IAAI,kBAAkB,GAAG;GAEjC,KAAK,oBAAoB,GAAG;EAC9B;EAEA,OAAO,OAAO,SAAS,SAAS,SAAS;CAC3C;CAEA,YAAY,QAA0D;EACpE,OAAO,KAAK,OACV;GACE,KAAK,cAAc,OAAO,aAAa,OAAO,WAAW;GACzD,UAAU;EACZ,SACM,KAAK,cAAc,MAAM,CACjC;CACF;AACF"}
@@ -69,7 +69,7 @@ var CpanDatasource = class CpanDatasource extends Datasource {
69
69
  getReleases(config) {
70
70
  return withCache({
71
71
  namespace: `datasource-${CpanDatasource.id}`,
72
- key: `${config.packageName}`,
72
+ key: config.packageName,
73
73
  cacheable: true,
74
74
  fallback: true
75
75
  }, () => this._getReleases(config));
@@ -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,GAAG,OAAO;GACf,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 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"}
@@ -3,6 +3,7 @@ import { newlineRegex, regEx } from "../../../util/regex.js";
3
3
  import { GlobalConfig } from "../../../config/global.js";
4
4
  import { logger } from "../../../logger/index.js";
5
5
  import { isHttpUrl, joinUrlParts, parseUrl } from "../../../util/url.js";
6
+ import { buildCacheKey } from "../../../util/cache/package/key.js";
6
7
  import { toSha256 } from "../../../util/hash.js";
7
8
  import { privateCacheDir, readCacheFile } from "../../../util/fs/index.js";
8
9
  import { Json } from "../../../util/schema-utils/index.js";
@@ -72,18 +73,26 @@ var CrateDatasource = class CrateDatasource extends Datasource {
72
73
  getReleases(config) {
73
74
  return withCache({
74
75
  namespace: `datasource-${CrateDatasource.id}`,
75
- key: `${config.registryUrl}/${config.packageName}`,
76
+ key: buildCacheKey(config.registryUrl, config.packageName),
76
77
  cacheable: CrateDatasource.isCratesIo(config.registryUrl),
77
78
  fallback: true
78
79
  }, () => this._getReleases(config));
79
80
  }
81
+ /** Returns the memory cache key of the registry's config.json. */
82
+ static registryConfigCacheKey(rawUrl) {
83
+ return buildCacheKey("crate-datasource", "registry-config", rawUrl);
84
+ }
85
+ /** Returns the memory cache key of the unsupported read API marker. */
86
+ static apiUnsupportedCacheKey(api) {
87
+ return buildCacheKey("crate-datasource", "registry-api-unsupported", api);
88
+ }
80
89
  /**
81
90
  * Fetches the registry's config.json which provides the API base URL
82
91
  * and download URL template.
83
92
  * See: https://doc.rust-lang.org/cargo/reference/registry-index.html#index-configuration
84
93
  */
85
94
  async fetchRegistryConfig(info) {
86
- const cacheKey = `crate-datasource/registry-config/${info.rawUrl}`;
95
+ const cacheKey = CrateDatasource.registryConfigCacheKey(info.rawUrl);
87
96
  const cached = get(cacheKey);
88
97
  if (cached) return cached;
89
98
  if (info.clonePath) try {
@@ -128,7 +137,7 @@ var CrateDatasource = class CrateDatasource extends Datasource {
128
137
  */
129
138
  async isReadApiUnsupported(api) {
130
139
  if (CrateDatasource.isCratesIoApi(api)) return false;
131
- const memKey = `crate-datasource/registry-api-unsupported/${api}`;
140
+ const memKey = CrateDatasource.apiUnsupportedCacheKey(api);
132
141
  const cached = get(memKey);
133
142
  if (isBoolean(cached)) return cached;
134
143
  const unsupported = await get$1("datasource-crate-registry-api", api) === true;
@@ -140,7 +149,7 @@ var CrateDatasource = class CrateDatasource extends Datasource {
140
149
  registryUrl,
141
150
  api
142
151
  }, "Registry does not implement the crates.io read API, skipping crate metadata and release timestamp lookups");
143
- set(`crate-datasource/registry-api-unsupported/${api}`, true);
152
+ set(CrateDatasource.apiUnsupportedCacheKey(api), true);
144
153
  await set$1("datasource-crate-registry-api", api, true, 1440);
145
154
  }
146
155
  async _getCrateMetadata(info, packageName) {
@@ -173,7 +182,7 @@ var CrateDatasource = class CrateDatasource extends Datasource {
173
182
  getCrateMetadata(info, packageName) {
174
183
  return withCache({
175
184
  namespace: `datasource-${CrateDatasource.id}-metadata`,
176
- key: `${info.rawUrl}/${packageName}`,
185
+ key: buildCacheKey(info.rawUrl, packageName),
177
186
  cacheable: info.flavor === "crates.io",
178
187
  ttlMinutes: 1440
179
188
  }, () => this._getCrateMetadata(info, packageName));
@@ -252,7 +261,7 @@ var CrateDatasource = class CrateDatasource extends Datasource {
252
261
  logger.warn("crate datasource: allowCustomCrateGitRegistries=true is required for git-cloning registries other than crates.io, bailing out");
253
262
  return null;
254
263
  }
255
- const cacheKey = `crate-datasource/registry-clone-path/${registryFetchUrl}`;
264
+ const cacheKey = buildCacheKey("crate-datasource", "registry-clone-path", registryFetchUrl);
256
265
  const lockKey = registryFetchUrl;
257
266
  const executionTimeout = GlobalConfig.get("executionTimeout") * 60 * 1e3;
258
267
  const gitTimeout = GlobalConfig.get("gitTimeout") || executionTimeout;
@@ -352,8 +361,7 @@ var CrateDatasource = class CrateDatasource extends Datasource {
352
361
  if (release.releaseTimestamp) return release;
353
362
  const rawUrl = registryUrl?.replace(regEx(/^sparse\+/), "");
354
363
  if (!rawUrl) return release;
355
- const cacheKey = `crate-datasource/registry-config/${rawUrl}`;
356
- const config = get(cacheKey);
364
+ const config = get(CrateDatasource.registryConfigCacheKey(rawUrl));
357
365
  if (!config?.api) return release;
358
366
  const { api } = config;
359
367
  if (await this.isReadApiUnsupported(api)) return release;
@@ -376,7 +384,7 @@ var CrateDatasource = class CrateDatasource extends Datasource {
376
384
  postprocessRelease(config, release) {
377
385
  return withCache({
378
386
  namespace: `datasource-crate`,
379
- key: `postprocessRelease:${config.registryUrl}:${config.packageName}:${release.version}`,
387
+ key: buildCacheKey("postprocessRelease", config.registryUrl, config.packageName, release.version),
380
388
  ttlMinutes: 10080,
381
389
  cacheable: CrateDatasource.isCratesIo(config.registryUrl ?? void 0)
382
390
  }, () => this._postprocessRelease(config, release));
@@ -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 { 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 // TODO: types (#22198)\n key: `${config.registryUrl}/${config.packageName}`,\n cacheable: CrateDatasource.isCratesIo(config.registryUrl),\n fallback: true,\n },\n () => this._getReleases(config),\n );\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 = `crate-datasource/registry-config/${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 = `crate-datasource/registry-api-unsupported/${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(`crate-datasource/registry-api-unsupported/${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: `${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 = `crate-datasource/registry-clone-path/${registryFetchUrl}`;\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 authentication: { hostTypes: [CrateDatasource.id] },\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 cacheKey = `crate-datasource/registry-config/${rawUrl}`;\n const config = memCache.get<RegistryConfig>(cacheKey);\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: `postprocessRelease:${config.registryUrl}:${config.packageName}:${release.version}`,\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 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 authentication: { hostTypes: [CrateDatasource.id] },\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,4 +1,5 @@
1
1
  import { logger } from "../../../logger/index.js";
2
+ import { buildCacheKey } from "../../../util/cache/package/key.js";
2
3
  import { createCacheReadStream } from "../../../util/fs/index.js";
3
4
  import { Datasource } from "../datasource.js";
4
5
  import { packageKeys, requiredPackageKeys } from "./common.js";
@@ -77,7 +78,7 @@ var DebDatasource = class DebDatasource extends Datasource {
77
78
  }
78
79
  parseExtractedPackageIndex(extractedFile, lastTimestamp) {
79
80
  return this.cached({
80
- key: `${extractedFile}:${lastTimestamp.getTime()}`,
81
+ key: buildCacheKey(extractedFile, lastTimestamp.getTime()),
81
82
  ttlMinutes: 1440
82
83
  }, () => this.readExtractedPackageIndex(extractedFile, lastTimestamp));
83
84
  }
@@ -120,7 +121,7 @@ var DebDatasource = class DebDatasource extends Datasource {
120
121
  }
121
122
  getReleases(config) {
122
123
  return this.cached({
123
- key: `${config.registryUrl}:${config.packageName}`,
124
+ key: buildCacheKey(config.registryUrl, config.packageName),
124
125
  fallback: true
125
126
  }, () => this.fetchReleases(config));
126
127
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["fs.createCacheReadStream"],"sources":["../../../../lib/modules/datasource/deb/index.ts"],"sourcesContent":["import readline from 'node:readline';\nimport { logger } from '../../../logger/index.ts';\nimport * as fs from '../../../util/fs/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { packageKeys, requiredPackageKeys } from './common.ts';\nimport { downloadAndExtractPackage } from './packages.ts';\nimport {\n formatReleaseResult,\n releaseMetaInformationMatches,\n} from './release.ts';\nimport type { PackageDescription } from './types.ts';\nimport { constructComponentUrls } from './url.ts';\n\nexport class DebDatasource extends Datasource {\n static readonly id = 'deb';\n\n constructor() {\n super(DebDatasource.id);\n }\n\n /**\n * Users are able to specify custom Debian repositories as long as they follow\n * the Debian package repository format as specified here\n * @see{https://wiki.debian.org/DebianRepository/Format}\n */\n override supportsCustomRegistry(_packageName: string): boolean {\n return true;\n }\n\n /**\n * Users can specify multiple upstream repositories and the datasource will aggregate the release\n * @example\n * When specifying multiple dependencies both internal and external dependencies from internal/external artifactory\n */\n override readonly registryStrategy = 'merge';\n\n /**\n * The original apt source list file format is\n * deb uri distribution [component1] [component2] [...]\n * @see{https://wiki.debian.org/DebianRepository/Format}\n *\n * However, for Renovate, we require the registry URLs to be\n * valid URLs which is why the parameters are encoded in the URL.\n *\n * The following query parameters are required:\n * - components: comma separated list of components\n * - suite: stable, oldstable or other alias for a release, either this or release must be given like buster\n * - binaryArch: e.g. amd64 resolves to http://deb.debian.org/debian/dists/stable/non-free/binary-amd64/\n */\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return [\n 'https://deb.debian.org/debian?suite=stable&components=main,contrib,non-free&binaryArch=amd64',\n ];\n }\n\n override readonly defaultVersioning = 'deb';\n\n /**\n * Parses the extracted package index file.\n *\n * @param extractedFile - The path to the extracted package file.\n * @param lastTimestamp - The timestamp of the last modification.\n * @returns a list of packages with minimal Metadata.\n */\n private async readExtractedPackageIndex(\n extractedFile: string,\n _lastTimestamp: Date,\n ): Promise<Record<string, PackageDescription[]>> {\n // read line by line to avoid high memory consumption as the extracted Packages\n // files can be multiple MBs in size\n const rl = readline.createInterface({\n input: fs.createCacheReadStream(extractedFile),\n terminal: false,\n });\n\n let currentPackage: PackageDescription = {};\n // A Package Index can contain multiple Versions of the package on private Artifactory (e.g. Jfrog)\n const allPackages: Record<string, PackageDescription[]> = {};\n\n for await (const line of rl) {\n if (line === '') {\n // All information of the package are available, add to the list of packages\n // v8 ignore else -- needs a package index block missing a required key\n if (requiredPackageKeys.every((key) => key in currentPackage)) {\n if (!allPackages[currentPackage.Package!]) {\n allPackages[currentPackage.Package!] = [];\n }\n allPackages[currentPackage.Package!].push(currentPackage);\n currentPackage = {};\n }\n } else {\n for (const key of packageKeys) {\n if (line.startsWith(`${key}:`)) {\n currentPackage[key] = line.substring(key.length + 1).trim();\n break;\n }\n }\n }\n }\n\n // Check the last package after file reading is complete\n if (requiredPackageKeys.every((key) => key in currentPackage)) {\n // v8 ignore else -- needs the final block to repeat an earlier package\n if (!allPackages[currentPackage.Package!]) {\n allPackages[currentPackage.Package!] = [];\n }\n allPackages[currentPackage.Package!].push(currentPackage);\n }\n\n return allPackages;\n }\n\n parseExtractedPackageIndex(\n extractedFile: string,\n lastTimestamp: Date,\n ): Promise<Record<string, PackageDescription[]>> {\n return this.cached(\n {\n key: `${extractedFile}:${lastTimestamp.getTime()}`,\n ttlMinutes: 24 * 60,\n },\n () => this.readExtractedPackageIndex(extractedFile, lastTimestamp),\n );\n }\n\n private async fetchPackageIndex(\n componentUrl: string,\n ): Promise<Record<string, PackageDescription[]>> {\n const { extractedFile, lastTimestamp } = await downloadAndExtractPackage(\n componentUrl,\n this.http,\n );\n return await this.parseExtractedPackageIndex(extractedFile, lastTimestamp);\n }\n\n getPackageIndex(\n componentUrl: string,\n ): Promise<Record<string, PackageDescription[]>> {\n return this.cached(\n {\n key: componentUrl,\n },\n () => this.fetchPackageIndex(componentUrl),\n );\n }\n\n /**\n * Fetches the release information for a given package from the registry URL.\n *\n * @param config - Configuration for fetching releases.\n * @returns The release result if the package is found, otherwise null.\n */\n private async fetchReleases({\n registryUrl,\n packageName,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n const componentUrls = constructComponentUrls(registryUrl);\n let aggregatedRelease: ReleaseResult | null = null;\n\n for (const componentUrl of componentUrls) {\n try {\n const packageIndex = await this.getPackageIndex(componentUrl);\n const parsedPackages = packageIndex[packageName];\n\n if (parsedPackages) {\n const newRelease = formatReleaseResult(parsedPackages);\n if (aggregatedRelease === null) {\n aggregatedRelease = newRelease;\n } else {\n // v8 ignore else -- needs two component indexes with matching meta\n if (!releaseMetaInformationMatches(aggregatedRelease, newRelease)) {\n logger.warn(\n { packageName },\n 'Package occurred in more than one repository with different meta information. Aggregating releases anyway.',\n );\n }\n aggregatedRelease.releases.push(...newRelease.releases);\n }\n }\n } catch (error) {\n logger.debug(\n { componentUrl, err: error },\n 'Skipping component due to an error',\n );\n }\n }\n\n return aggregatedRelease;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return this.cached(\n {\n key: `${config.registryUrl}:${config.packageName}`,\n fallback: true,\n },\n () => this.fetchReleases(config),\n );\n }\n}\n"],"mappings":";;;;;;;;;AAcA,IAAa,gBAAb,MAAa,sBAAsB,WAAW;CAC5C,OAAgB,KAAK;CAErB,cAAc;EACZ,MAAM,cAAc,EAAE;CACxB;;;;;;CAOA,uBAAgC,cAA+B;EAC7D,OAAO;CACT;;;;;;CAOA,mBAAqC;;;;;;;;;;;;;;CAerC,uBAAgC,cAAgC;EAC9D,OAAO,CACL,8FACF;CACF;CAEA,oBAAsC;;;;;;;;CAStC,MAAc,0BACZ,eACA,gBAC+C;EAG/C,MAAM,KAAK,SAAS,gBAAgB;GAClC,OAAOA,sBAAyB,aAAa;GAC7C,UAAU;EACZ,CAAC;EAED,IAAI,iBAAqC,CAAC;EAE1C,MAAM,cAAoD,CAAC;EAE3D,WAAW,MAAM,QAAQ,IACvB,IAAI,SAAS,IAGP;;OAAA,oBAAoB,OAAO,QAAQ,OAAO,cAAc,GAAG;IAC7D,IAAI,CAAC,YAAY,eAAe,UAC9B,YAAY,eAAe,WAAY,CAAC;IAE1C,YAAY,eAAe,QAAS,CAAC,KAAK,cAAc;IACxD,iBAAiB,CAAC;GACpB;SAEA,KAAK,MAAM,OAAO,aAChB,IAAI,KAAK,WAAW,GAAG,IAAI,EAAE,GAAG;GAC9B,eAAe,OAAO,KAAK,UAAU,IAAI,SAAS,CAAC,CAAC,CAAC,KAAK;GAC1D;EACF;EAMN,IAAI,oBAAoB,OAAO,QAAQ,OAAO,cAAc,GAAG;;GAE7D,IAAI,CAAC,YAAY,eAAe,UAC9B,YAAY,eAAe,WAAY,CAAC;GAE1C,YAAY,eAAe,QAAS,CAAC,KAAK,cAAc;EAC1D;EAEA,OAAO;CACT;CAEA,2BACE,eACA,eAC+C;EAC/C,OAAO,KAAK,OACV;GACE,KAAK,GAAG,cAAc,GAAG,cAAc,QAAQ;GAC/C,YAAY;EACd,SACM,KAAK,0BAA0B,eAAe,aAAa,CACnE;CACF;CAEA,MAAc,kBACZ,cAC+C;EAC/C,MAAM,EAAE,eAAe,kBAAkB,MAAM,0BAC7C,cACA,KAAK,IACP;EACA,OAAO,MAAM,KAAK,2BAA2B,eAAe,aAAa;CAC3E;CAEA,gBACE,cAC+C;EAC/C,OAAO,KAAK,OACV,EACE,KAAK,aACP,SACM,KAAK,kBAAkB,YAAY,CAC3C;CACF;;;;;;;CAQA,MAAc,cAAc,EAC1B,aACA,eACmD;;EAEnD,IAAI,CAAC,aACH,OAAO;EAGT,MAAM,gBAAgB,uBAAuB,WAAW;EACxD,IAAI,oBAA0C;EAE9C,KAAK,MAAM,gBAAgB,eACzB,IAAI;GAEF,MAAM,kBAAiB,MADI,KAAK,gBAAgB,YAAY,EAAA,CACxB;GAEpC,IAAI,gBAAgB;IAClB,MAAM,aAAa,oBAAoB,cAAc;IACrD,IAAI,sBAAsB,MACxB,oBAAoB;SACf;;KAEL,IAAI,CAAC,8BAA8B,mBAAmB,UAAU,GAC9D,OAAO,KACL,EAAE,YAAY,GACd,4GACF;KAEF,kBAAkB,SAAS,KAAK,GAAG,WAAW,QAAQ;IACxD;GACF;EACF,SAAS,OAAO;GACd,OAAO,MACL;IAAE;IAAc,KAAK;GAAM,GAC3B,oCACF;EACF;EAGF,OAAO;CACT;CAEA,YAAY,QAA0D;EACpE,OAAO,KAAK,OACV;GACE,KAAK,GAAG,OAAO,YAAY,GAAG,OAAO;GACrC,UAAU;EACZ,SACM,KAAK,cAAc,MAAM,CACjC;CACF;AACF"}
1
+ {"version":3,"file":"index.js","names":["fs.createCacheReadStream"],"sources":["../../../../lib/modules/datasource/deb/index.ts"],"sourcesContent":["import readline from 'node:readline';\nimport { logger } from '../../../logger/index.ts';\nimport { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport * as fs from '../../../util/fs/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { packageKeys, requiredPackageKeys } from './common.ts';\nimport { downloadAndExtractPackage } from './packages.ts';\nimport {\n formatReleaseResult,\n releaseMetaInformationMatches,\n} from './release.ts';\nimport type { PackageDescription } from './types.ts';\nimport { constructComponentUrls } from './url.ts';\n\nexport class DebDatasource extends Datasource {\n static readonly id = 'deb';\n\n constructor() {\n super(DebDatasource.id);\n }\n\n /**\n * Users are able to specify custom Debian repositories as long as they follow\n * the Debian package repository format as specified here\n * @see{https://wiki.debian.org/DebianRepository/Format}\n */\n override supportsCustomRegistry(_packageName: string): boolean {\n return true;\n }\n\n /**\n * Users can specify multiple upstream repositories and the datasource will aggregate the release\n * @example\n * When specifying multiple dependencies both internal and external dependencies from internal/external artifactory\n */\n override readonly registryStrategy = 'merge';\n\n /**\n * The original apt source list file format is\n * deb uri distribution [component1] [component2] [...]\n * @see{https://wiki.debian.org/DebianRepository/Format}\n *\n * However, for Renovate, we require the registry URLs to be\n * valid URLs which is why the parameters are encoded in the URL.\n *\n * The following query parameters are required:\n * - components: comma separated list of components\n * - suite: stable, oldstable or other alias for a release, either this or release must be given like buster\n * - binaryArch: e.g. amd64 resolves to http://deb.debian.org/debian/dists/stable/non-free/binary-amd64/\n */\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return [\n 'https://deb.debian.org/debian?suite=stable&components=main,contrib,non-free&binaryArch=amd64',\n ];\n }\n\n override readonly defaultVersioning = 'deb';\n\n /**\n * Parses the extracted package index file.\n *\n * @param extractedFile - The path to the extracted package file.\n * @param lastTimestamp - The timestamp of the last modification.\n * @returns a list of packages with minimal Metadata.\n */\n private async readExtractedPackageIndex(\n extractedFile: string,\n _lastTimestamp: Date,\n ): Promise<Record<string, PackageDescription[]>> {\n // read line by line to avoid high memory consumption as the extracted Packages\n // files can be multiple MBs in size\n const rl = readline.createInterface({\n input: fs.createCacheReadStream(extractedFile),\n terminal: false,\n });\n\n let currentPackage: PackageDescription = {};\n // A Package Index can contain multiple Versions of the package on private Artifactory (e.g. Jfrog)\n const allPackages: Record<string, PackageDescription[]> = {};\n\n for await (const line of rl) {\n if (line === '') {\n // All information of the package are available, add to the list of packages\n // v8 ignore else -- needs a package index block missing a required key\n if (requiredPackageKeys.every((key) => key in currentPackage)) {\n if (!allPackages[currentPackage.Package!]) {\n allPackages[currentPackage.Package!] = [];\n }\n allPackages[currentPackage.Package!].push(currentPackage);\n currentPackage = {};\n }\n } else {\n for (const key of packageKeys) {\n if (line.startsWith(`${key}:`)) {\n currentPackage[key] = line.substring(key.length + 1).trim();\n break;\n }\n }\n }\n }\n\n // Check the last package after file reading is complete\n if (requiredPackageKeys.every((key) => key in currentPackage)) {\n // v8 ignore else -- needs the final block to repeat an earlier package\n if (!allPackages[currentPackage.Package!]) {\n allPackages[currentPackage.Package!] = [];\n }\n allPackages[currentPackage.Package!].push(currentPackage);\n }\n\n return allPackages;\n }\n\n parseExtractedPackageIndex(\n extractedFile: string,\n lastTimestamp: Date,\n ): Promise<Record<string, PackageDescription[]>> {\n return this.cached(\n {\n key: buildCacheKey(extractedFile, lastTimestamp.getTime()),\n ttlMinutes: 24 * 60,\n },\n () => this.readExtractedPackageIndex(extractedFile, lastTimestamp),\n );\n }\n\n private async fetchPackageIndex(\n componentUrl: string,\n ): Promise<Record<string, PackageDescription[]>> {\n const { extractedFile, lastTimestamp } = await downloadAndExtractPackage(\n componentUrl,\n this.http,\n );\n return await this.parseExtractedPackageIndex(extractedFile, lastTimestamp);\n }\n\n getPackageIndex(\n componentUrl: string,\n ): Promise<Record<string, PackageDescription[]>> {\n return this.cached(\n {\n key: componentUrl,\n },\n () => this.fetchPackageIndex(componentUrl),\n );\n }\n\n /**\n * Fetches the release information for a given package from the registry URL.\n *\n * @param config - Configuration for fetching releases.\n * @returns The release result if the package is found, otherwise null.\n */\n private async fetchReleases({\n registryUrl,\n packageName,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n const componentUrls = constructComponentUrls(registryUrl);\n let aggregatedRelease: ReleaseResult | null = null;\n\n for (const componentUrl of componentUrls) {\n try {\n const packageIndex = await this.getPackageIndex(componentUrl);\n const parsedPackages = packageIndex[packageName];\n\n if (parsedPackages) {\n const newRelease = formatReleaseResult(parsedPackages);\n if (aggregatedRelease === null) {\n aggregatedRelease = newRelease;\n } else {\n // v8 ignore else -- needs two component indexes with matching meta\n if (!releaseMetaInformationMatches(aggregatedRelease, newRelease)) {\n logger.warn(\n { packageName },\n 'Package occurred in more than one repository with different meta information. Aggregating releases anyway.',\n );\n }\n aggregatedRelease.releases.push(...newRelease.releases);\n }\n }\n } catch (error) {\n logger.debug(\n { componentUrl, err: error },\n 'Skipping component due to an error',\n );\n }\n }\n\n return aggregatedRelease;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return this.cached(\n {\n key: buildCacheKey(config.registryUrl, config.packageName),\n fallback: true,\n },\n () => this.fetchReleases(config),\n );\n }\n}\n"],"mappings":";;;;;;;;;;AAeA,IAAa,gBAAb,MAAa,sBAAsB,WAAW;CAC5C,OAAgB,KAAK;CAErB,cAAc;EACZ,MAAM,cAAc,EAAE;CACxB;;;;;;CAOA,uBAAgC,cAA+B;EAC7D,OAAO;CACT;;;;;;CAOA,mBAAqC;;;;;;;;;;;;;;CAerC,uBAAgC,cAAgC;EAC9D,OAAO,CACL,8FACF;CACF;CAEA,oBAAsC;;;;;;;;CAStC,MAAc,0BACZ,eACA,gBAC+C;EAG/C,MAAM,KAAK,SAAS,gBAAgB;GAClC,OAAOA,sBAAyB,aAAa;GAC7C,UAAU;EACZ,CAAC;EAED,IAAI,iBAAqC,CAAC;EAE1C,MAAM,cAAoD,CAAC;EAE3D,WAAW,MAAM,QAAQ,IACvB,IAAI,SAAS,IAGP;;OAAA,oBAAoB,OAAO,QAAQ,OAAO,cAAc,GAAG;IAC7D,IAAI,CAAC,YAAY,eAAe,UAC9B,YAAY,eAAe,WAAY,CAAC;IAE1C,YAAY,eAAe,QAAS,CAAC,KAAK,cAAc;IACxD,iBAAiB,CAAC;GACpB;SAEA,KAAK,MAAM,OAAO,aAChB,IAAI,KAAK,WAAW,GAAG,IAAI,EAAE,GAAG;GAC9B,eAAe,OAAO,KAAK,UAAU,IAAI,SAAS,CAAC,CAAC,CAAC,KAAK;GAC1D;EACF;EAMN,IAAI,oBAAoB,OAAO,QAAQ,OAAO,cAAc,GAAG;;GAE7D,IAAI,CAAC,YAAY,eAAe,UAC9B,YAAY,eAAe,WAAY,CAAC;GAE1C,YAAY,eAAe,QAAS,CAAC,KAAK,cAAc;EAC1D;EAEA,OAAO;CACT;CAEA,2BACE,eACA,eAC+C;EAC/C,OAAO,KAAK,OACV;GACE,KAAK,cAAc,eAAe,cAAc,QAAQ,CAAC;GACzD,YAAY;EACd,SACM,KAAK,0BAA0B,eAAe,aAAa,CACnE;CACF;CAEA,MAAc,kBACZ,cAC+C;EAC/C,MAAM,EAAE,eAAe,kBAAkB,MAAM,0BAC7C,cACA,KAAK,IACP;EACA,OAAO,MAAM,KAAK,2BAA2B,eAAe,aAAa;CAC3E;CAEA,gBACE,cAC+C;EAC/C,OAAO,KAAK,OACV,EACE,KAAK,aACP,SACM,KAAK,kBAAkB,YAAY,CAC3C;CACF;;;;;;;CAQA,MAAc,cAAc,EAC1B,aACA,eACmD;;EAEnD,IAAI,CAAC,aACH,OAAO;EAGT,MAAM,gBAAgB,uBAAuB,WAAW;EACxD,IAAI,oBAA0C;EAE9C,KAAK,MAAM,gBAAgB,eACzB,IAAI;GAEF,MAAM,kBAAiB,MADI,KAAK,gBAAgB,YAAY,EAAA,CACxB;GAEpC,IAAI,gBAAgB;IAClB,MAAM,aAAa,oBAAoB,cAAc;IACrD,IAAI,sBAAsB,MACxB,oBAAoB;SACf;;KAEL,IAAI,CAAC,8BAA8B,mBAAmB,UAAU,GAC9D,OAAO,KACL,EAAE,YAAY,GACd,4GACF;KAEF,kBAAkB,SAAS,KAAK,GAAG,WAAW,QAAQ;IACxD;GACF;EACF,SAAS,OAAO;GACd,OAAO,MACL;IAAE;IAAc,KAAK;GAAM,GAC3B,oCACF;EACF;EAGF,OAAO;CACT;CAEA,YAAY,QAA0D;EACpE,OAAO,KAAK,OACV;GACE,KAAK,cAAc,OAAO,aAAa,OAAO,WAAW;GACzD,UAAU;EACZ,SACM,KAAK,cAAc,MAAM,CACjC;CACF;AACF"}
@@ -2,6 +2,7 @@ import { coerceObject } from "../../../util/object.js";
2
2
  import { regEx } from "../../../util/regex.js";
3
3
  import { logger } from "../../../logger/index.js";
4
4
  import { joinUrlParts } from "../../../util/url.js";
5
+ import { buildCacheKey } from "../../../util/cache/package/key.js";
5
6
  import { id } from "../../versioning/semver/index.js";
6
7
  import { get, set } from "../../../util/cache/package/index.js";
7
8
  import { withCache } from "../../../util/cache/package/with-cache.js";
@@ -40,12 +41,12 @@ var DenoDatasource = class DenoDatasource extends Datasource {
40
41
  getReleases(config) {
41
42
  return withCache({
42
43
  namespace: `datasource-${DenoDatasource.id}`,
43
- key: `getReleases:${config.registryUrl}:${config.packageName}`,
44
+ key: buildCacheKey("getReleases", config.registryUrl, config.packageName),
44
45
  fallback: true
45
46
  }, () => this._getReleases(config));
46
47
  }
47
48
  async _getReleaseResult(moduleAPIURL) {
48
- const detailsCacheKey = `details:${moduleAPIURL}`;
49
+ const detailsCacheKey = buildCacheKey("details", moduleAPIURL);
49
50
  const releasesCache = coerceObject(await get(`datasource-${DenoDatasource.id}`, detailsCacheKey));
50
51
  let cacheModified = false;
51
52
  const { body: { versions, tags } } = await this.http.getJson(moduleAPIURL, DenoAPIModuleResponse);
@@ -75,7 +76,7 @@ var DenoDatasource = class DenoDatasource extends Datasource {
75
76
  getReleaseResult(moduleAPIURL) {
76
77
  return withCache({
77
78
  namespace: `datasource-${DenoDatasource.id}`,
78
- key: `getReleaseResult:${moduleAPIURL}`
79
+ key: buildCacheKey("getReleaseResult", moduleAPIURL)
79
80
  }, () => this._getReleaseResult(moduleAPIURL));
80
81
  }
81
82
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["semanticVersioning.id","packageCache.get","packageCache.set"],"sources":["../../../../lib/modules/datasource/deno/index.ts"],"sourcesContent":["import { isNullOrUndefined } from '@sindresorhus/is';\nimport pMap from 'p-map';\nimport { logger } from '../../../logger/index.ts';\nimport * as packageCache from '../../../util/cache/package/index.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { coerceObject } from '../../../util/object.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { joinUrlParts } from '../../../util/url.ts';\nimport * as semanticVersioning from '../../versioning/semver/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { Release } from '../index.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport {\n DenoAPIModuleResponse,\n DenoAPIModuleVersionResponse,\n} from './schema.ts';\n\nexport class DenoDatasource extends Datasource {\n static readonly id = 'deno';\n\n override supportsCustomRegistry(_packageName: string): boolean {\n return true;\n }\n\n override readonly defaultVersioning = semanticVersioning.id;\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return ['https://apiland.deno.dev'];\n }\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `uploaded_at` field in the results.';\n override readonly sourceUrlSupport = 'release';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `repository` field in the results.';\n\n constructor() {\n super(DenoDatasource.id);\n }\n\n private async _getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n const massagedRegistryUrl = registryUrl!;\n\n const extractResult = regEx(\n /^(?:https:\\/\\/deno.land\\/)(?<rawPackageName>[^@\\s]+)/,\n ).exec(packageName);\n const rawPackageName = extractResult?.groups?.rawPackageName;\n if (isNullOrUndefined(rawPackageName)) {\n logger.debug(\n `Could not extract rawPackageName from packageName: \"${packageName}\"`,\n );\n return null;\n }\n\n // remove third-party prefix if defined. The only internal library is `std` and is available under the same API\n const massagedPackageName = rawPackageName.replace('x/', '');\n\n // https://apiland.deno.dev/v2/modules/postgres\n const moduleAPIURL = joinUrlParts(\n massagedRegistryUrl,\n 'v2/modules',\n massagedPackageName,\n );\n\n return await this.getReleaseResult(moduleAPIURL);\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${DenoDatasource.id}`,\n // TODO: types (#22198)\n key: `getReleases:${config.registryUrl}:${config.packageName}`,\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n\n private async _getReleaseResult(\n moduleAPIURL: string,\n ): Promise<ReleaseResult> {\n const detailsCacheKey = `details:${moduleAPIURL}`;\n const releasesCache: Record<string, Release> = coerceObject(\n await packageCache.get(\n `datasource-${DenoDatasource.id}`,\n detailsCacheKey,\n ),\n );\n let cacheModified = false;\n\n const {\n body: { versions, tags },\n } = await this.http.getJson(moduleAPIURL, DenoAPIModuleResponse);\n\n // get details for the versions\n const releases = await pMap(\n versions,\n async (version) => {\n const cacheRelease = releasesCache[version];\n /* v8 ignore next: hard to test */\n if (cacheRelease) {\n return cacheRelease;\n }\n\n // https://apiland.deno.dev/v2/modules/postgres/v0.17.0\n const url = joinUrlParts(moduleAPIURL, version);\n const { body: release } = await this.http.getJson(\n url,\n DenoAPIModuleVersionResponse.catch(({ error: err }) => {\n logger.warn(\n { err, version },\n 'Deno: failed to get version details',\n );\n return { version };\n }),\n );\n\n releasesCache[release.version] = release;\n cacheModified = true;\n\n return release;\n },\n { concurrency: 5 },\n );\n\n // v8 ignore else -- needs every release already present in the cache\n if (cacheModified) {\n // 1 week. Releases at Deno are immutable, therefore we can use a long term cache here.\n await packageCache.set(\n `datasource-${DenoDatasource.id}`,\n detailsCacheKey,\n releasesCache,\n 10080,\n );\n }\n\n return { releases, tags };\n }\n\n getReleaseResult(moduleAPIURL: string): Promise<ReleaseResult> {\n return withCache(\n {\n namespace: `datasource-${DenoDatasource.id}`,\n key: `getReleaseResult:${moduleAPIURL}`,\n },\n () => this._getReleaseResult(moduleAPIURL),\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,IAAa,iBAAb,MAAa,uBAAuB,WAAW;CAC7C,OAAgB,KAAK;CAErB,uBAAgC,cAA+B;EAC7D,OAAO;CACT;CAEA,oBAAsCA;CAEtC,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,0BAA0B;CACpC;CAEA,0BAA4C;CAC5C,uBACE;CACF,mBAAqC;CACrC,gBACE;CAEF,cAAc;EACZ,MAAM,eAAe,EAAE;CACzB;CAEA,MAAc,aAAa,EACzB,aACA,eACmD;EACnD,MAAM,sBAAsB;EAK5B,MAAM,iBAHgB,MACpB,sDACF,CAAC,CAAC,KAAK,WAC4B,CAAC,EAAE,QAAQ;EAC9C,IAAI,kBAAkB,cAAc,GAAG;GACrC,OAAO,MACL,uDAAuD,YAAY,EACrE;GACA,OAAO;EACT;EAGA,MAAM,sBAAsB,eAAe,QAAQ,MAAM,EAAE;EAG3D,MAAM,eAAe,aACnB,qBACA,cACA,mBACF;EAEA,OAAO,MAAM,KAAK,iBAAiB,YAAY;CACjD;CAEA,YAAY,QAA0D;EACpE,OAAO,UACL;GACE,WAAW,cAAc,eAAe;GAExC,KAAK,eAAe,OAAO,YAAY,GAAG,OAAO;GACjD,UAAU;EACZ,SACM,KAAK,aAAa,MAAM,CAChC;CACF;CAEA,MAAc,kBACZ,cACwB;EACxB,MAAM,kBAAkB,WAAW;EACnC,MAAM,gBAAyC,aAC7C,MAAMC,IACJ,cAAc,eAAe,MAC7B,eACF,CACF;EACA,IAAI,gBAAgB;EAEpB,MAAM,EACJ,MAAM,EAAE,UAAU,WAChB,MAAM,KAAK,KAAK,QAAQ,cAAc,qBAAqB;EAG/D,MAAM,WAAW,MAAM,KACrB,UACA,OAAO,YAAY;GACjB,MAAM,eAAe,cAAc;;GAEnC,IAAI,cACF,OAAO;GAIT,MAAM,MAAM,aAAa,cAAc,OAAO;GAC9C,MAAM,EAAE,MAAM,YAAY,MAAM,KAAK,KAAK,QACxC,KACA,6BAA6B,OAAO,EAAE,OAAO,UAAU;IACrD,OAAO,KACL;KAAE;KAAK;IAAQ,GACf,qCACF;IACA,OAAO,EAAE,QAAQ;GACnB,CAAC,CACH;GAEA,cAAc,QAAQ,WAAW;GACjC,gBAAgB;GAEhB,OAAO;EACT,GACA,EAAE,aAAa,EAAE,CACnB;;EAGA,IAAI,eAEF,MAAMC,IACJ,cAAc,eAAe,MAC7B,iBACA,eACA,KACF;EAGF,OAAO;GAAE;GAAU;EAAK;CAC1B;CAEA,iBAAiB,cAA8C;EAC7D,OAAO,UACL;GACE,WAAW,cAAc,eAAe;GACxC,KAAK,oBAAoB;EAC3B,SACM,KAAK,kBAAkB,YAAY,CAC3C;CACF;AACF"}
1
+ {"version":3,"file":"index.js","names":["semanticVersioning.id","packageCache.get","packageCache.set"],"sources":["../../../../lib/modules/datasource/deno/index.ts"],"sourcesContent":["import { isNullOrUndefined } from '@sindresorhus/is';\nimport pMap from 'p-map';\nimport { logger } from '../../../logger/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 { coerceObject } from '../../../util/object.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { joinUrlParts } from '../../../util/url.ts';\nimport * as semanticVersioning from '../../versioning/semver/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { Release } from '../index.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport {\n DenoAPIModuleResponse,\n DenoAPIModuleVersionResponse,\n} from './schema.ts';\n\nexport class DenoDatasource extends Datasource {\n static readonly id = 'deno';\n\n override supportsCustomRegistry(_packageName: string): boolean {\n return true;\n }\n\n override readonly defaultVersioning = semanticVersioning.id;\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return ['https://apiland.deno.dev'];\n }\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `uploaded_at` field in the results.';\n override readonly sourceUrlSupport = 'release';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `repository` field in the results.';\n\n constructor() {\n super(DenoDatasource.id);\n }\n\n private async _getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n const massagedRegistryUrl = registryUrl!;\n\n const extractResult = regEx(\n /^(?:https:\\/\\/deno.land\\/)(?<rawPackageName>[^@\\s]+)/,\n ).exec(packageName);\n const rawPackageName = extractResult?.groups?.rawPackageName;\n if (isNullOrUndefined(rawPackageName)) {\n logger.debug(\n `Could not extract rawPackageName from packageName: \"${packageName}\"`,\n );\n return null;\n }\n\n // remove third-party prefix if defined. The only internal library is `std` and is available under the same API\n const massagedPackageName = rawPackageName.replace('x/', '');\n\n // https://apiland.deno.dev/v2/modules/postgres\n const moduleAPIURL = joinUrlParts(\n massagedRegistryUrl,\n 'v2/modules',\n massagedPackageName,\n );\n\n return await this.getReleaseResult(moduleAPIURL);\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${DenoDatasource.id}`,\n key: buildCacheKey(\n 'getReleases',\n config.registryUrl,\n config.packageName,\n ),\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n\n private async _getReleaseResult(\n moduleAPIURL: string,\n ): Promise<ReleaseResult> {\n const detailsCacheKey = buildCacheKey('details', moduleAPIURL);\n const releasesCache: Record<string, Release> = coerceObject(\n await packageCache.get(\n `datasource-${DenoDatasource.id}`,\n detailsCacheKey,\n ),\n );\n let cacheModified = false;\n\n const {\n body: { versions, tags },\n } = await this.http.getJson(moduleAPIURL, DenoAPIModuleResponse);\n\n // get details for the versions\n const releases = await pMap(\n versions,\n async (version) => {\n const cacheRelease = releasesCache[version];\n /* v8 ignore next: hard to test */\n if (cacheRelease) {\n return cacheRelease;\n }\n\n // https://apiland.deno.dev/v2/modules/postgres/v0.17.0\n const url = joinUrlParts(moduleAPIURL, version);\n const { body: release } = await this.http.getJson(\n url,\n DenoAPIModuleVersionResponse.catch(({ error: err }) => {\n logger.warn(\n { err, version },\n 'Deno: failed to get version details',\n );\n return { version };\n }),\n );\n\n releasesCache[release.version] = release;\n cacheModified = true;\n\n return release;\n },\n { concurrency: 5 },\n );\n\n // v8 ignore else -- needs every release already present in the cache\n if (cacheModified) {\n // 1 week. Releases at Deno are immutable, therefore we can use a long term cache here.\n await packageCache.set(\n `datasource-${DenoDatasource.id}`,\n detailsCacheKey,\n releasesCache,\n 10080,\n );\n }\n\n return { releases, tags };\n }\n\n getReleaseResult(moduleAPIURL: string): Promise<ReleaseResult> {\n return withCache(\n {\n namespace: `datasource-${DenoDatasource.id}`,\n key: buildCacheKey('getReleaseResult', moduleAPIURL),\n },\n () => this._getReleaseResult(moduleAPIURL),\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAkBA,IAAa,iBAAb,MAAa,uBAAuB,WAAW;CAC7C,OAAgB,KAAK;CAErB,uBAAgC,cAA+B;EAC7D,OAAO;CACT;CAEA,oBAAsCA;CAEtC,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,0BAA0B;CACpC;CAEA,0BAA4C;CAC5C,uBACE;CACF,mBAAqC;CACrC,gBACE;CAEF,cAAc;EACZ,MAAM,eAAe,EAAE;CACzB;CAEA,MAAc,aAAa,EACzB,aACA,eACmD;EACnD,MAAM,sBAAsB;EAK5B,MAAM,iBAHgB,MACpB,sDACF,CAAC,CAAC,KAAK,WAC4B,CAAC,EAAE,QAAQ;EAC9C,IAAI,kBAAkB,cAAc,GAAG;GACrC,OAAO,MACL,uDAAuD,YAAY,EACrE;GACA,OAAO;EACT;EAGA,MAAM,sBAAsB,eAAe,QAAQ,MAAM,EAAE;EAG3D,MAAM,eAAe,aACnB,qBACA,cACA,mBACF;EAEA,OAAO,MAAM,KAAK,iBAAiB,YAAY;CACjD;CAEA,YAAY,QAA0D;EACpE,OAAO,UACL;GACE,WAAW,cAAc,eAAe;GACxC,KAAK,cACH,eACA,OAAO,aACP,OAAO,WACT;GACA,UAAU;EACZ,SACM,KAAK,aAAa,MAAM,CAChC;CACF;CAEA,MAAc,kBACZ,cACwB;EACxB,MAAM,kBAAkB,cAAc,WAAW,YAAY;EAC7D,MAAM,gBAAyC,aAC7C,MAAMC,IACJ,cAAc,eAAe,MAC7B,eACF,CACF;EACA,IAAI,gBAAgB;EAEpB,MAAM,EACJ,MAAM,EAAE,UAAU,WAChB,MAAM,KAAK,KAAK,QAAQ,cAAc,qBAAqB;EAG/D,MAAM,WAAW,MAAM,KACrB,UACA,OAAO,YAAY;GACjB,MAAM,eAAe,cAAc;;GAEnC,IAAI,cACF,OAAO;GAIT,MAAM,MAAM,aAAa,cAAc,OAAO;GAC9C,MAAM,EAAE,MAAM,YAAY,MAAM,KAAK,KAAK,QACxC,KACA,6BAA6B,OAAO,EAAE,OAAO,UAAU;IACrD,OAAO,KACL;KAAE;KAAK;IAAQ,GACf,qCACF;IACA,OAAO,EAAE,QAAQ;GACnB,CAAC,CACH;GAEA,cAAc,QAAQ,WAAW;GACjC,gBAAgB;GAEhB,OAAO;EACT,GACA,EAAE,aAAa,EAAE,CACnB;;EAGA,IAAI,eAEF,MAAMC,IACJ,cAAc,eAAe,MAC7B,iBACA,eACA,KACF;EAGF,OAAO;GAAE;GAAU;EAAK;CAC1B;CAEA,iBAAiB,cAA8C;EAC7D,OAAO,UACL;GACE,WAAW,cAAc,eAAe;GACxC,KAAK,cAAc,oBAAoB,YAAY;EACrD,SACM,KAAK,kBAAkB,YAAY,CAC3C;CACF;AACF"}