renovate 44.210.2 → 44.211.0

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 (307) 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/utils.js +2 -1
  4. package/dist/config/utils.js.map +1 -1
  5. package/dist/config/validation-helpers/regex-glob-matchers.js.map +1 -1
  6. package/dist/config/validation-helpers/utils.js +5 -1
  7. package/dist/config/validation-helpers/utils.js.map +1 -1
  8. package/dist/config/validation.js +8 -2
  9. package/dist/config/validation.js.map +1 -1
  10. package/dist/config-validator.js +1 -1
  11. package/dist/config-validator.js.map +1 -1
  12. package/dist/global-config-option-defaults.generated.js +1 -1
  13. package/dist/global-config-option-defaults.generated.js.map +1 -1
  14. package/dist/instrumentation/index.js +3 -2
  15. package/dist/instrumentation/index.js.map +1 -1
  16. package/dist/logger/bunyan.js +3 -2
  17. package/dist/logger/bunyan.js.map +1 -1
  18. package/dist/logger/pretty-stdout.js +2 -1
  19. package/dist/logger/pretty-stdout.js.map +1 -1
  20. package/dist/modules/datasource/apk/index.js +2 -2
  21. package/dist/modules/datasource/apk/index.js.map +1 -1
  22. package/dist/modules/datasource/artifactory/index.js +2 -3
  23. package/dist/modules/datasource/artifactory/index.js.map +1 -1
  24. package/dist/modules/datasource/bazel/index.js +2 -1
  25. package/dist/modules/datasource/bazel/index.js.map +1 -1
  26. package/dist/modules/datasource/cdnjs/index.js +2 -1
  27. package/dist/modules/datasource/cdnjs/index.js.map +1 -1
  28. package/dist/modules/datasource/conda/index.js +2 -1
  29. package/dist/modules/datasource/conda/index.js.map +1 -1
  30. package/dist/modules/datasource/crate/index.js +2 -1
  31. package/dist/modules/datasource/crate/index.js.map +1 -1
  32. package/dist/modules/datasource/dart-version/index.js +2 -1
  33. package/dist/modules/datasource/dart-version/index.js.map +1 -1
  34. package/dist/modules/datasource/datasource.js +2 -2
  35. package/dist/modules/datasource/datasource.js.map +1 -1
  36. package/dist/modules/datasource/deb/url.js +3 -1
  37. package/dist/modules/datasource/deb/url.js.map +1 -1
  38. package/dist/modules/datasource/devbox/index.js +2 -1
  39. package/dist/modules/datasource/devbox/index.js.map +1 -1
  40. package/dist/modules/datasource/docker/common.js +10 -8
  41. package/dist/modules/datasource/docker/common.js.map +1 -1
  42. package/dist/modules/datasource/docker/ecr.js +2 -1
  43. package/dist/modules/datasource/docker/ecr.js.map +1 -1
  44. package/dist/modules/datasource/docker/index.js +20 -17
  45. package/dist/modules/datasource/docker/index.js.map +1 -1
  46. package/dist/modules/datasource/gitea-tags/base.js +2 -3
  47. package/dist/modules/datasource/gitea-tags/base.js.map +1 -1
  48. package/dist/modules/datasource/github-tags/index.js +2 -1
  49. package/dist/modules/datasource/github-tags/index.js.map +1 -1
  50. package/dist/modules/datasource/gitlab-tags/index.js +2 -3
  51. package/dist/modules/datasource/gitlab-tags/index.js.map +1 -1
  52. package/dist/modules/datasource/go/base.js +1 -1
  53. package/dist/modules/datasource/go/base.js.map +1 -1
  54. package/dist/modules/datasource/go/common.js +8 -2
  55. package/dist/modules/datasource/go/common.js.map +1 -1
  56. package/dist/modules/datasource/go/env.js +2 -1
  57. package/dist/modules/datasource/go/env.js.map +1 -1
  58. package/dist/modules/datasource/go/releases-goproxy.js +7 -5
  59. package/dist/modules/datasource/go/releases-goproxy.js.map +1 -1
  60. package/dist/modules/datasource/helm/index.js +2 -1
  61. package/dist/modules/datasource/helm/index.js.map +1 -1
  62. package/dist/modules/datasource/hexpm-bob/index.js +2 -1
  63. package/dist/modules/datasource/hexpm-bob/index.js.map +1 -1
  64. package/dist/modules/datasource/index.js +4 -3
  65. package/dist/modules/datasource/index.js.map +1 -1
  66. package/dist/modules/datasource/java-version/adoptium.js +3 -2
  67. package/dist/modules/datasource/java-version/adoptium.js.map +1 -1
  68. package/dist/modules/datasource/java-version/graalvm.js +2 -1
  69. package/dist/modules/datasource/java-version/graalvm.js.map +1 -1
  70. package/dist/modules/datasource/maven/index.js.map +1 -1
  71. package/dist/modules/datasource/maven/util.js +7 -10
  72. package/dist/modules/datasource/maven/util.js.map +1 -1
  73. package/dist/modules/datasource/npm/npmrc.js +2 -2
  74. package/dist/modules/datasource/npm/npmrc.js.map +1 -1
  75. package/dist/modules/datasource/nuget/v3.js +3 -5
  76. package/dist/modules/datasource/nuget/v3.js.map +1 -1
  77. package/dist/modules/datasource/packagist/index.js +4 -5
  78. package/dist/modules/datasource/packagist/index.js.map +1 -1
  79. package/dist/modules/datasource/pod/index.js +4 -4
  80. package/dist/modules/datasource/pod/index.js.map +1 -1
  81. package/dist/modules/datasource/pypi/index.js +19 -11
  82. package/dist/modules/datasource/pypi/index.js.map +1 -1
  83. package/dist/modules/datasource/python-version/index.js +2 -3
  84. package/dist/modules/datasource/python-version/index.js.map +1 -1
  85. package/dist/modules/datasource/repology/index.js +4 -3
  86. package/dist/modules/datasource/repology/index.js.map +1 -1
  87. package/dist/modules/datasource/rpm/providers/common.js +3 -1
  88. package/dist/modules/datasource/rpm/providers/common.js.map +1 -1
  89. package/dist/modules/datasource/rubygems/index.js +2 -5
  90. package/dist/modules/datasource/rubygems/index.js.map +1 -1
  91. package/dist/modules/datasource/rubygems/versions-endpoint-cache.js +13 -18
  92. package/dist/modules/datasource/rubygems/versions-endpoint-cache.js.map +1 -1
  93. package/dist/modules/datasource/terraform-module/base.js +2 -1
  94. package/dist/modules/datasource/terraform-module/base.js.map +1 -1
  95. package/dist/modules/datasource/terraform-provider/index.js +2 -3
  96. package/dist/modules/datasource/terraform-provider/index.js.map +1 -1
  97. package/dist/modules/manager/bazel-module/parser/context.js +0 -1
  98. package/dist/modules/manager/bazel-module/parser/context.js.map +1 -1
  99. package/dist/modules/manager/bundler/host-rules.js +4 -1
  100. package/dist/modules/manager/bundler/host-rules.js.map +1 -1
  101. package/dist/modules/manager/composer/range.js +1 -1
  102. package/dist/modules/manager/composer/range.js.map +1 -1
  103. package/dist/modules/manager/custom/utils.js +3 -1
  104. package/dist/modules/manager/custom/utils.js.map +1 -1
  105. package/dist/modules/manager/devbox/schema.js +2 -1
  106. package/dist/modules/manager/devbox/schema.js.map +1 -1
  107. package/dist/modules/manager/dockerfile/extract.js +10 -2
  108. package/dist/modules/manager/dockerfile/extract.js.map +1 -1
  109. package/dist/modules/manager/fingerprint.generated.js +1 -1
  110. package/dist/modules/manager/fingerprint.generated.js.map +1 -1
  111. package/dist/modules/manager/github-actions/parse.js +2 -1
  112. package/dist/modules/manager/github-actions/parse.js.map +1 -1
  113. package/dist/modules/manager/gomod/artifacts.js +5 -2
  114. package/dist/modules/manager/gomod/artifacts.js.map +1 -1
  115. package/dist/modules/manager/gradle/artifacts.js +5 -2
  116. package/dist/modules/manager/gradle/artifacts.js.map +1 -1
  117. package/dist/modules/manager/gradle/utils.js +2 -1
  118. package/dist/modules/manager/gradle/utils.js.map +1 -1
  119. package/dist/modules/manager/gradle-wrapper/utils.js +9 -5
  120. package/dist/modules/manager/gradle-wrapper/utils.js.map +1 -1
  121. package/dist/modules/manager/helm-values/util.js +1 -1
  122. package/dist/modules/manager/helm-values/util.js.map +1 -1
  123. package/dist/modules/manager/helmfile/artifacts.js +1 -1
  124. package/dist/modules/manager/helmfile/artifacts.js.map +1 -1
  125. package/dist/modules/manager/helmv3/artifacts.js +1 -1
  126. package/dist/modules/manager/helmv3/artifacts.js.map +1 -1
  127. package/dist/modules/manager/maven/extract.js +5 -2
  128. package/dist/modules/manager/maven/extract.js.map +1 -1
  129. package/dist/modules/manager/maven-wrapper/artifacts.js +3 -2
  130. package/dist/modules/manager/maven-wrapper/artifacts.js.map +1 -1
  131. package/dist/modules/manager/mise/artifacts.js +2 -1
  132. package/dist/modules/manager/mise/artifacts.js.map +1 -1
  133. package/dist/modules/manager/mise/extract.js.map +1 -1
  134. package/dist/modules/manager/npm/artifacts.js +3 -1
  135. package/dist/modules/manager/npm/artifacts.js.map +1 -1
  136. package/dist/modules/manager/npm/extract/common/dependency.js +4 -1
  137. package/dist/modules/manager/npm/extract/common/dependency.js.map +1 -1
  138. package/dist/modules/manager/npm/extract/common/package-file.js +2 -1
  139. package/dist/modules/manager/npm/extract/common/package-file.js.map +1 -1
  140. package/dist/modules/manager/npm/extract/index.js.map +1 -1
  141. package/dist/modules/manager/npm/extract/post/locked-versions.js.map +1 -1
  142. package/dist/modules/manager/npm/npmrc.js +2 -1
  143. package/dist/modules/manager/npm/npmrc.js.map +1 -1
  144. package/dist/modules/manager/npm/post-update/index.js +2 -1
  145. package/dist/modules/manager/npm/post-update/index.js.map +1 -1
  146. package/dist/modules/manager/npm/post-update/npm.js +2 -3
  147. package/dist/modules/manager/npm/post-update/npm.js.map +1 -1
  148. package/dist/modules/manager/npm/post-update/yarn.js.map +1 -1
  149. package/dist/modules/manager/npm/schema.js +6 -2
  150. package/dist/modules/manager/npm/schema.js.map +1 -1
  151. package/dist/modules/manager/npm/update/dependency/index.js +3 -2
  152. package/dist/modules/manager/npm/update/dependency/index.js.map +1 -1
  153. package/dist/modules/manager/npm/update/locked-dependency/common/parent-version.js +6 -2
  154. package/dist/modules/manager/npm/update/locked-dependency/common/parent-version.js.map +1 -1
  155. package/dist/modules/manager/npm/update/locked-dependency/package-lock/get-locked.js +2 -1
  156. package/dist/modules/manager/npm/update/locked-dependency/package-lock/get-locked.js.map +1 -1
  157. package/dist/modules/manager/nuget/package-tree.js +2 -1
  158. package/dist/modules/manager/nuget/package-tree.js.map +1 -1
  159. package/dist/modules/manager/paket/update-locked.js +4 -1
  160. package/dist/modules/manager/paket/update-locked.js.map +1 -1
  161. package/dist/modules/manager/pipenv/extract.js +4 -1
  162. package/dist/modules/manager/pipenv/extract.js.map +1 -1
  163. package/dist/modules/platform/azure/azure-helper.js +2 -1
  164. package/dist/modules/platform/azure/azure-helper.js.map +1 -1
  165. package/dist/modules/platform/azure/index.js +7 -3
  166. package/dist/modules/platform/azure/index.js.map +1 -1
  167. package/dist/modules/platform/bitbucket/index.js +13 -6
  168. package/dist/modules/platform/bitbucket/index.js.map +1 -1
  169. package/dist/modules/platform/bitbucket-server/index.js +13 -9
  170. package/dist/modules/platform/bitbucket-server/index.js.map +1 -1
  171. package/dist/modules/platform/codecommit/index.js +2 -2
  172. package/dist/modules/platform/codecommit/index.js.map +1 -1
  173. package/dist/modules/platform/gerrit/client.js +4 -1
  174. package/dist/modules/platform/gerrit/client.js.map +1 -1
  175. package/dist/modules/platform/gerrit/index.js +3 -2
  176. package/dist/modules/platform/gerrit/index.js.map +1 -1
  177. package/dist/modules/platform/gitea/gitea-helper.js +5 -4
  178. package/dist/modules/platform/gitea/gitea-helper.js.map +1 -1
  179. package/dist/modules/platform/gitea/index.js +11 -4
  180. package/dist/modules/platform/gitea/index.js.map +1 -1
  181. package/dist/modules/platform/github/index.js +36 -23
  182. package/dist/modules/platform/github/index.js.map +1 -1
  183. package/dist/modules/platform/gitlab/index.js +16 -9
  184. package/dist/modules/platform/gitlab/index.js.map +1 -1
  185. package/dist/modules/platform/scm-manager/index.js +4 -1
  186. package/dist/modules/platform/scm-manager/index.js.map +1 -1
  187. package/dist/modules/platform/scm-manager/utils.js +2 -1
  188. package/dist/modules/platform/scm-manager/utils.js.map +1 -1
  189. package/dist/modules/versioning/cargo/index.js.map +1 -1
  190. package/dist/modules/versioning/composer/index.js +2 -1
  191. package/dist/modules/versioning/composer/index.js.map +1 -1
  192. package/dist/modules/versioning/conan/common.js +4 -1
  193. package/dist/modules/versioning/conan/common.js.map +1 -1
  194. package/dist/modules/versioning/debian/index.js +2 -1
  195. package/dist/modules/versioning/debian/index.js.map +1 -1
  196. package/dist/modules/versioning/gradle/compare.js +2 -1
  197. package/dist/modules/versioning/gradle/compare.js.map +1 -1
  198. package/dist/modules/versioning/hex/index.js +2 -1
  199. package/dist/modules/versioning/hex/index.js.map +1 -1
  200. package/dist/modules/versioning/julia/index.js.map +1 -1
  201. package/dist/modules/versioning/maven/compare.js +17 -7
  202. package/dist/modules/versioning/maven/compare.js.map +1 -1
  203. package/dist/modules/versioning/npm/index.js +2 -1
  204. package/dist/modules/versioning/npm/index.js.map +1 -1
  205. package/dist/modules/versioning/npm/range.js.map +1 -1
  206. package/dist/modules/versioning/paket/index.js +2 -1
  207. package/dist/modules/versioning/paket/index.js.map +1 -1
  208. package/dist/modules/versioning/paket/range.js +7 -3
  209. package/dist/modules/versioning/paket/range.js.map +1 -1
  210. package/dist/modules/versioning/pep440/index.js +2 -1
  211. package/dist/modules/versioning/pep440/index.js.map +1 -1
  212. package/dist/modules/versioning/poetry/index.js +2 -1
  213. package/dist/modules/versioning/poetry/index.js.map +1 -1
  214. package/dist/modules/versioning/regex/index.js +1 -1
  215. package/dist/modules/versioning/regex/index.js.map +1 -1
  216. package/dist/modules/versioning/rez/index.js +2 -1
  217. package/dist/modules/versioning/rez/index.js.map +1 -1
  218. package/dist/modules/versioning/vcpkg/index.js +2 -1
  219. package/dist/modules/versioning/vcpkg/index.js.map +1 -1
  220. package/dist/util/cache/package/impl/base.js +2 -1
  221. package/dist/util/cache/package/impl/base.js.map +1 -1
  222. package/dist/util/cache/package/with-cache.js +2 -1
  223. package/dist/util/cache/package/with-cache.js.map +1 -1
  224. package/dist/util/check-token.js +7 -4
  225. package/dist/util/check-token.js.map +1 -1
  226. package/dist/util/exec/utils.js +4 -1
  227. package/dist/util/exec/utils.js.map +1 -1
  228. package/dist/util/git/auth.js +5 -1
  229. package/dist/util/git/auth.js.map +1 -1
  230. package/dist/util/git/error.js.map +1 -1
  231. package/dist/util/host-rules.js +5 -3
  232. package/dist/util/host-rules.js.map +1 -1
  233. package/dist/util/http/cache/abstract-http-cache-provider.js +3 -2
  234. package/dist/util/http/cache/abstract-http-cache-provider.js.map +1 -1
  235. package/dist/util/http/cache/package-http-cache-provider.js +2 -1
  236. package/dist/util/http/cache/package-http-cache-provider.js.map +1 -1
  237. package/dist/util/http/github.js +15 -11
  238. package/dist/util/http/github.js.map +1 -1
  239. package/dist/util/http/gitlab.js +5 -4
  240. package/dist/util/http/gitlab.js.map +1 -1
  241. package/dist/util/http/http.js +2 -1
  242. package/dist/util/http/http.js.map +1 -1
  243. package/dist/util/http/jwt.js +2 -1
  244. package/dist/util/http/jwt.js.map +1 -1
  245. package/dist/util/http/retry-after.js +2 -1
  246. package/dist/util/http/retry-after.js.map +1 -1
  247. package/dist/util/http/status.js +95 -0
  248. package/dist/util/http/status.js.map +1 -0
  249. package/dist/util/merge-confidence/index.js +3 -2
  250. package/dist/util/merge-confidence/index.js.map +1 -1
  251. package/dist/util/package-rules/current-version.js +4 -1
  252. package/dist/util/package-rules/current-version.js.map +1 -1
  253. package/dist/util/package-rules/index.js +7 -3
  254. package/dist/util/package-rules/index.js.map +1 -1
  255. package/dist/util/schema-utils/index.js +5 -0
  256. package/dist/util/schema-utils/index.js.map +1 -1
  257. package/dist/workers/global/config/parse/host-rules-from-env.js +2 -1
  258. package/dist/workers/global/config/parse/host-rules-from-env.js.map +1 -1
  259. package/dist/workers/repository/common.js +4 -1
  260. package/dist/workers/repository/common.js.map +1 -1
  261. package/dist/workers/repository/config-migration/branch/index.js +2 -1
  262. package/dist/workers/repository/config-migration/branch/index.js.map +1 -1
  263. package/dist/workers/repository/dependency-dashboard.js +6 -2
  264. package/dist/workers/repository/dependency-dashboard.js.map +1 -1
  265. package/dist/workers/repository/finalize/repository-statistics.js +4 -1
  266. package/dist/workers/repository/finalize/repository-statistics.js.map +1 -1
  267. package/dist/workers/repository/init/merge.js +13 -3
  268. package/dist/workers/repository/init/merge.js.map +1 -1
  269. package/dist/workers/repository/onboarding/branch/check.js +2 -1
  270. package/dist/workers/repository/onboarding/branch/check.js.map +1 -1
  271. package/dist/workers/repository/onboarding/pr/pr-list.js +4 -2
  272. package/dist/workers/repository/onboarding/pr/pr-list.js.map +1 -1
  273. package/dist/workers/repository/process/extract-update.js +4 -1
  274. package/dist/workers/repository/process/extract-update.js.map +1 -1
  275. package/dist/workers/repository/process/lookup/digest.js +2 -1
  276. package/dist/workers/repository/process/lookup/digest.js.map +1 -1
  277. package/dist/workers/repository/process/lookup/generate.js +1 -1
  278. package/dist/workers/repository/process/lookup/generate.js.map +1 -1
  279. package/dist/workers/repository/process/lookup/index.js +4 -1
  280. package/dist/workers/repository/process/lookup/index.js.map +1 -1
  281. package/dist/workers/repository/process/lookup/utils.js +7 -1
  282. package/dist/workers/repository/process/lookup/utils.js.map +1 -1
  283. package/dist/workers/repository/process/vulnerabilities.js +10 -3
  284. package/dist/workers/repository/process/vulnerabilities.js.map +1 -1
  285. package/dist/workers/repository/reconfigure/index.js +2 -1
  286. package/dist/workers/repository/reconfigure/index.js.map +1 -1
  287. package/dist/workers/repository/update/branch/auto-replace.js +6 -2
  288. package/dist/workers/repository/update/branch/auto-replace.js.map +1 -1
  289. package/dist/workers/repository/update/branch/errors.js +2 -1
  290. package/dist/workers/repository/update/branch/errors.js.map +1 -1
  291. package/dist/workers/repository/update/branch/execute-post-upgrade-commands.js +2 -2
  292. package/dist/workers/repository/update/branch/execute-post-upgrade-commands.js.map +1 -1
  293. package/dist/workers/repository/update/branch/get-updated.js +1 -2
  294. package/dist/workers/repository/update/branch/get-updated.js.map +1 -1
  295. package/dist/workers/repository/update/branch/index.js +2 -1
  296. package/dist/workers/repository/update/branch/index.js.map +1 -1
  297. package/dist/workers/repository/update/branch/status-checks.js +2 -1
  298. package/dist/workers/repository/update/branch/status-checks.js.map +1 -1
  299. package/dist/workers/repository/update/pr/body/index.js.map +1 -1
  300. package/dist/workers/repository/update/pr/changelog/release-notes.js +3 -2
  301. package/dist/workers/repository/update/pr/changelog/release-notes.js.map +1 -1
  302. package/dist/workers/repository/update/pr/index.js +7 -3
  303. package/dist/workers/repository/update/pr/index.js.map +1 -1
  304. package/dist/workers/repository/updates/generate.js +6 -3
  305. package/dist/workers/repository/updates/generate.js.map +1 -1
  306. package/package.json +2 -2
  307. package/renovate-schema.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"v3.js","names":["packageCache.get","packageCache.set","p.all","versioning","HttpError","fs.createCacheWriteStream","fs.pipeline","fs.readCacheFile","fs.rmCache"],"sources":["../../../../lib/modules/datasource/nuget/v3.ts"],"sourcesContent":["import { isNonEmptyString } from '@sindresorhus/is';\nimport AdmZip from 'adm-zip';\nimport semver from 'semver';\nimport upath from 'upath';\nimport { XmlDocument } from 'xmldoc';\nimport { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { coerceArray } from '../../../util/array.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 { getEnv } from '../../../util/env.ts';\nimport * as fs from '../../../util/fs/index.ts';\nimport { ensureCacheDir } from '../../../util/fs/index.ts';\nimport { memCacheProvider } from '../../../util/http/cache/memory-http-cache-provider.ts';\nimport type { Http } from '../../../util/http/index.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport * as p from '../../../util/promises.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport { ensureTrailingSlash } from '../../../util/url.ts';\nimport { api as versioning } from '../../versioning/nuget/index.ts';\nimport type { Release, ReleaseResult } from '../types.ts';\nimport { massageUrl, removeBuildMeta, sortNugetVersions } from './common.ts';\nimport type { CatalogEntry } from './schema.ts';\nimport {\n CatalogPage,\n PackageRegistration,\n ServicesIndexRaw,\n} from './schema.ts';\n\nexport class NugetV3Api {\n static readonly cacheNamespace = 'datasource-nuget-v3';\n\n async getResourceUrl(\n http: Http,\n url: string,\n resourceType = 'RegistrationsBaseUrl',\n ): Promise<string | null> {\n // https://learn.microsoft.com/nuget/api/service-index\n const resultCacheKey = buildCacheKey(url, resourceType);\n const cachedResult = await packageCache.get<string>(\n NugetV3Api.cacheNamespace,\n resultCacheKey,\n );\n\n /* v8 ignore next -- TODO: add test */\n if (cachedResult) {\n return cachedResult;\n }\n let servicesIndexRaw: ServicesIndexRaw | undefined;\n try {\n const responseCacheKey = url;\n servicesIndexRaw = await packageCache.get<ServicesIndexRaw>(\n NugetV3Api.cacheNamespace,\n responseCacheKey,\n );\n // v8 ignore else -- needs a primed package cache\n if (!servicesIndexRaw) {\n servicesIndexRaw = (\n await http.getJson(\n url,\n { cacheProvider: memCacheProvider },\n ServicesIndexRaw,\n )\n ).body;\n await packageCache.set(\n NugetV3Api.cacheNamespace,\n responseCacheKey,\n servicesIndexRaw,\n 3 * 24 * 60,\n );\n }\n\n const services = servicesIndexRaw.resources\n .map(({ '@id': serviceId, '@type': t }) => ({\n serviceId,\n type: t?.split('/')?.shift(),\n version: t?.split('/')?.pop(),\n }))\n .filter(\n ({ type, version }) => type === resourceType && semver.valid(version),\n )\n .sort((x, y) =>\n x.version && y.version\n ? semver.compare(x.version, y.version)\n : /* istanbul ignore next: hard to test */ 0,\n );\n\n if (services.length === 0) {\n await packageCache.set(\n NugetV3Api.cacheNamespace,\n resultCacheKey,\n null,\n 60,\n );\n logger.debug(\n { url, servicesIndexRaw },\n `no ${resourceType} services found`,\n );\n return null;\n }\n\n const { serviceId, version } = services.pop()!;\n\n // istanbul ignore if\n if (\n resourceType === 'RegistrationsBaseUrl' &&\n version &&\n !version.startsWith('3.0.0-') &&\n !semver.satisfies(version, '^3.0.0')\n ) {\n logger.warn(\n { url, version },\n `Nuget: Unknown version returned. Only v3 is supported`,\n );\n }\n\n await packageCache.set(\n NugetV3Api.cacheNamespace,\n resultCacheKey,\n serviceId,\n 60,\n );\n return serviceId;\n } catch (err) {\n // istanbul ignore if: not easy testable with nock\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug(\n { err, url, servicesIndexRaw },\n `nuget registry failure: can't get ${resourceType}`,\n );\n return null;\n }\n }\n\n async getCatalogEntry(\n http: Http,\n catalogPage: CatalogPage,\n ): Promise<CatalogEntry[]> {\n let items = catalogPage.items;\n if (!items) {\n const url = catalogPage['@id'];\n if (!url) {\n return [];\n }\n const catalogPageFull = await http.getJson(url, CatalogPage);\n items = catalogPageFull.body.items;\n }\n return coerceArray(items).map(({ catalogEntry }) => catalogEntry);\n }\n\n async getReleases(\n http: Http,\n registryUrl: string,\n feedUrl: string,\n pkgName: string,\n ): Promise<ReleaseResult | null> {\n const baseUrl = feedUrl.replace(regEx(/\\/*$/), '');\n const url = `${baseUrl}/${pkgName.toLowerCase()}/index.json`;\n const packageRegistration = await http.getJson(url, PackageRegistration);\n const catalogPages = packageRegistration.body.items;\n const catalogPagesQueue = catalogPages.map(\n (page) => (): Promise<CatalogEntry[]> => this.getCatalogEntry(http, page),\n );\n const catalogEntries = (await p.all(catalogPagesQueue))\n .flat()\n .sort((a, b) => sortNugetVersions(a.version, b.version));\n\n let homepage: string | null = null;\n let latestStable: string | null = null;\n let nupkgUrl: string | null = null;\n const releases = catalogEntries.map(\n ({\n version,\n published,\n projectUrl,\n listed,\n packageContent,\n deprecation,\n }) => {\n const release: Release = { version: removeBuildMeta(version) };\n const releaseTimestamp = asTimestamp(published);\n if (releaseTimestamp) {\n release.releaseTimestamp = releaseTimestamp;\n }\n if (\n versioning.isValid(version) &&\n versioning.isStable(version) &&\n listed\n ) {\n latestStable = removeBuildMeta(version);\n homepage = projectUrl ? massageUrl(projectUrl) : homepage;\n nupkgUrl = massageUrl(packageContent);\n }\n\n if (listed === false || deprecation) {\n release.isDeprecated = true;\n }\n return release;\n },\n );\n\n if (!releases.length) {\n return null;\n }\n\n // istanbul ignore next: only happens when no stable version exists\n if (latestStable === null && catalogPages.length) {\n const last = catalogEntries.pop()!;\n latestStable = removeBuildMeta(last.version);\n homepage ??= last.projectUrl ?? null;\n nupkgUrl ??= massageUrl(last.packageContent);\n }\n\n const dep: ReleaseResult = {\n releases,\n };\n\n if (releases.every((release) => release.isDeprecated === true)) {\n dep.deprecationMessage = this.getDeprecationMessage(pkgName);\n }\n\n try {\n const packageBaseAddress = await this.getResourceUrl(\n http,\n registryUrl,\n 'PackageBaseAddress',\n );\n let shouldTryNupkg = false;\n if (isNonEmptyString(packageBaseAddress)) {\n const nuspecUrl = `${ensureTrailingSlash(\n packageBaseAddress,\n )}${pkgName.toLowerCase()}/${\n latestStable\n }/${pkgName.toLowerCase()}.nuspec`;\n try {\n const metaresult = await http.getText(nuspecUrl, {\n cacheProvider: memCacheProvider,\n });\n const nuspec = new XmlDocument(metaresult.body);\n const releaseNotes = nuspec.valueWithPath('metadata.releaseNotes');\n if (releaseNotes) {\n dep.changelogContent = releaseNotes;\n }\n const sourceUrl = nuspec.valueWithPath('metadata.repository@url');\n if (sourceUrl) {\n dep.sourceUrl = massageUrl(sourceUrl);\n }\n } catch (err) {\n /* v8 ignore else -- not easy testable with nock */\n if (err instanceof HttpError && err.response?.statusCode === 404) {\n shouldTryNupkg = true;\n logger.debug(\n { registryUrl, pkgName, pkgVersion: latestStable },\n `package manifest (.nuspec) not found`,\n );\n } else {\n throw err;\n }\n }\n } else {\n shouldTryNupkg = true;\n }\n\n if (!dep.sourceUrl && shouldTryNupkg && nupkgUrl) {\n const sourceUrl = await this.getSourceUrlFromNupkg(\n http,\n registryUrl,\n pkgName,\n latestStable,\n nupkgUrl,\n );\n if (sourceUrl) {\n dep.sourceUrl = massageUrl(sourceUrl);\n logger.debug(`Determined sourceUrl ${sourceUrl} from ${nupkgUrl}`);\n }\n }\n } catch (err) {\n // istanbul ignore if: not easy testable with nock\n if (err instanceof ExternalHostError) {\n throw err;\n }\n // ignore / silence 404. Seen on proget, if remote connector is used and package is not yet cached\n if (err instanceof HttpError && err.response?.statusCode === 404) {\n logger.debug(\n { registryUrl, pkgName, pkgVersion: latestStable },\n `package manifest (.nuspec) not found`,\n );\n } else {\n logger.debug(\n { err, registryUrl, pkgName, pkgVersion: latestStable },\n `Cannot obtain sourceUrl`,\n );\n }\n }\n\n if (homepage) {\n // only assign if not assigned\n dep.sourceUrl ??= homepage;\n dep.homepage ??= homepage;\n }\n\n return dep;\n }\n\n private async _getSourceUrlFromNupkg(\n http: Http,\n _registryUrl: string,\n packageName: string,\n packageVersion: string | null,\n nupkgUrl: string,\n ): Promise<string | null> {\n /* v8 ignore if -- specs enable RENOVATE_X_NUGET_DOWNLOAD_NUPKGS, so the disabled path is unexercised */\n if (!getEnv().RENOVATE_X_NUGET_DOWNLOAD_NUPKGS) {\n logger.once.debug('RENOVATE_X_NUGET_DOWNLOAD_NUPKGS is not set');\n return null;\n }\n const cacheDir = await ensureCacheDir('nuget');\n const nupkgFile = upath.join(\n cacheDir,\n `${packageName}.${packageVersion}.nupkg`,\n );\n const nupkgContentsDir = upath.join(\n cacheDir,\n `${packageName}.${packageVersion}`,\n );\n const readStream = http.stream(nupkgUrl);\n try {\n const writeStream = fs.createCacheWriteStream(nupkgFile);\n await fs.pipeline(readStream, writeStream);\n const zip = new AdmZip(nupkgFile);\n zip.extractAllTo(nupkgContentsDir);\n const nuspecFile = upath.join(nupkgContentsDir, `${packageName}.nuspec`);\n const nuspec = new XmlDocument(\n await fs.readCacheFile(nuspecFile, 'utf8'),\n );\n return nuspec.valueWithPath('metadata.repository@url') ?? null;\n } finally {\n await fs.rmCache(nupkgFile);\n await fs.rmCache(nupkgContentsDir);\n }\n }\n\n getSourceUrlFromNupkg(\n http: Http,\n registryUrl: string,\n packageName: string,\n packageVersion: string | null,\n nupkgUrl: string,\n ): Promise<string | null> {\n return withCache(\n {\n namespace: NugetV3Api.cacheNamespace,\n key: buildCacheKey('source-url', registryUrl, packageName),\n ttlMinutes: 10080, // 1 week\n },\n () =>\n this._getSourceUrlFromNupkg(\n http,\n registryUrl,\n packageName,\n packageVersion,\n nupkgUrl,\n ),\n );\n }\n\n getDeprecationMessage(packageName: string): string {\n return `The package \\`${packageName}\\` is deprecated.`;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"v3.js","names":["packageCache.get","packageCache.set","p.all","versioning","fs.createCacheWriteStream","fs.pipeline","fs.readCacheFile","fs.rmCache"],"sources":["../../../../lib/modules/datasource/nuget/v3.ts"],"sourcesContent":["import { isNonEmptyString } from '@sindresorhus/is';\nimport AdmZip from 'adm-zip';\nimport semver from 'semver';\nimport upath from 'upath';\nimport { XmlDocument } from 'xmldoc';\nimport { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { coerceArray } from '../../../util/array.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 { getEnv } from '../../../util/env.ts';\nimport * as fs from '../../../util/fs/index.ts';\nimport { ensureCacheDir } from '../../../util/fs/index.ts';\nimport { memCacheProvider } from '../../../util/http/cache/memory-http-cache-provider.ts';\nimport type { Http } from '../../../util/http/index.ts';\nimport { isNotFound } from '../../../util/http/status.ts';\nimport * as p from '../../../util/promises.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport { ensureTrailingSlash } from '../../../util/url.ts';\nimport { api as versioning } from '../../versioning/nuget/index.ts';\nimport type { Release, ReleaseResult } from '../types.ts';\nimport { massageUrl, removeBuildMeta, sortNugetVersions } from './common.ts';\nimport type { CatalogEntry } from './schema.ts';\nimport {\n CatalogPage,\n PackageRegistration,\n ServicesIndexRaw,\n} from './schema.ts';\n\nexport class NugetV3Api {\n static readonly cacheNamespace = 'datasource-nuget-v3';\n\n async getResourceUrl(\n http: Http,\n url: string,\n resourceType = 'RegistrationsBaseUrl',\n ): Promise<string | null> {\n // https://learn.microsoft.com/nuget/api/service-index\n const resultCacheKey = buildCacheKey(url, resourceType);\n const cachedResult = await packageCache.get<string>(\n NugetV3Api.cacheNamespace,\n resultCacheKey,\n );\n\n /* v8 ignore next -- TODO: add test */\n if (cachedResult) {\n return cachedResult;\n }\n let servicesIndexRaw: ServicesIndexRaw | undefined;\n try {\n const responseCacheKey = url;\n servicesIndexRaw = await packageCache.get<ServicesIndexRaw>(\n NugetV3Api.cacheNamespace,\n responseCacheKey,\n );\n // v8 ignore else -- needs a primed package cache\n if (!servicesIndexRaw) {\n servicesIndexRaw = (\n await http.getJson(\n url,\n { cacheProvider: memCacheProvider },\n ServicesIndexRaw,\n )\n ).body;\n await packageCache.set(\n NugetV3Api.cacheNamespace,\n responseCacheKey,\n servicesIndexRaw,\n 3 * 24 * 60,\n );\n }\n\n const services = servicesIndexRaw.resources\n .map(({ '@id': serviceId, '@type': t }) => ({\n serviceId,\n type: t?.split('/')?.shift(),\n version: t?.split('/')?.pop(),\n }))\n .filter(\n ({ type, version }) => type === resourceType && semver.valid(version),\n )\n .sort((x, y) =>\n x.version && y.version\n ? semver.compare(x.version, y.version)\n : /* istanbul ignore next: hard to test */ 0,\n );\n\n if (services.length === 0) {\n await packageCache.set(\n NugetV3Api.cacheNamespace,\n resultCacheKey,\n null,\n 60,\n );\n logger.debug(\n { url, servicesIndexRaw },\n `no ${resourceType} services found`,\n );\n return null;\n }\n\n const { serviceId, version } = services.pop()!;\n\n // istanbul ignore if\n if (\n resourceType === 'RegistrationsBaseUrl' &&\n version &&\n !version.startsWith('3.0.0-') &&\n !semver.satisfies(version, '^3.0.0')\n ) {\n logger.warn(\n { url, version },\n `Nuget: Unknown version returned. Only v3 is supported`,\n );\n }\n\n await packageCache.set(\n NugetV3Api.cacheNamespace,\n resultCacheKey,\n serviceId,\n 60,\n );\n return serviceId;\n } catch (err) {\n // istanbul ignore if: not easy testable with nock\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug(\n { err, url, servicesIndexRaw },\n `nuget registry failure: can't get ${resourceType}`,\n );\n return null;\n }\n }\n\n async getCatalogEntry(\n http: Http,\n catalogPage: CatalogPage,\n ): Promise<CatalogEntry[]> {\n let items = catalogPage.items;\n if (!items) {\n const url = catalogPage['@id'];\n if (!url) {\n return [];\n }\n const catalogPageFull = await http.getJson(url, CatalogPage);\n items = catalogPageFull.body.items;\n }\n return coerceArray(items).map(({ catalogEntry }) => catalogEntry);\n }\n\n async getReleases(\n http: Http,\n registryUrl: string,\n feedUrl: string,\n pkgName: string,\n ): Promise<ReleaseResult | null> {\n const baseUrl = feedUrl.replace(regEx(/\\/*$/), '');\n const url = `${baseUrl}/${pkgName.toLowerCase()}/index.json`;\n const packageRegistration = await http.getJson(url, PackageRegistration);\n const catalogPages = packageRegistration.body.items;\n const catalogPagesQueue = catalogPages.map(\n (page) => (): Promise<CatalogEntry[]> => this.getCatalogEntry(http, page),\n );\n const catalogEntries = (await p.all(catalogPagesQueue))\n .flat()\n .sort((a, b) => sortNugetVersions(a.version, b.version));\n\n let homepage: string | null = null;\n let latestStable: string | null = null;\n let nupkgUrl: string | null = null;\n const releases = catalogEntries.map(\n ({\n version,\n published,\n projectUrl,\n listed,\n packageContent,\n deprecation,\n }) => {\n const release: Release = { version: removeBuildMeta(version) };\n const releaseTimestamp = asTimestamp(published);\n if (releaseTimestamp) {\n release.releaseTimestamp = releaseTimestamp;\n }\n if (\n versioning.isValid(version) &&\n versioning.isStable(version) &&\n listed\n ) {\n latestStable = removeBuildMeta(version);\n homepage = projectUrl ? massageUrl(projectUrl) : homepage;\n nupkgUrl = massageUrl(packageContent);\n }\n\n if (listed === false || deprecation) {\n release.isDeprecated = true;\n }\n return release;\n },\n );\n\n if (!releases.length) {\n return null;\n }\n\n // istanbul ignore next: only happens when no stable version exists\n if (latestStable === null && catalogPages.length) {\n const last = catalogEntries.pop()!;\n latestStable = removeBuildMeta(last.version);\n homepage ??= last.projectUrl ?? null;\n nupkgUrl ??= massageUrl(last.packageContent);\n }\n\n const dep: ReleaseResult = {\n releases,\n };\n\n if (releases.every((release) => release.isDeprecated === true)) {\n dep.deprecationMessage = this.getDeprecationMessage(pkgName);\n }\n\n try {\n const packageBaseAddress = await this.getResourceUrl(\n http,\n registryUrl,\n 'PackageBaseAddress',\n );\n let shouldTryNupkg = false;\n if (isNonEmptyString(packageBaseAddress)) {\n const nuspecUrl = `${ensureTrailingSlash(\n packageBaseAddress,\n )}${pkgName.toLowerCase()}/${\n latestStable\n }/${pkgName.toLowerCase()}.nuspec`;\n try {\n const metaresult = await http.getText(nuspecUrl, {\n cacheProvider: memCacheProvider,\n });\n const nuspec = new XmlDocument(metaresult.body);\n const releaseNotes = nuspec.valueWithPath('metadata.releaseNotes');\n if (releaseNotes) {\n dep.changelogContent = releaseNotes;\n }\n const sourceUrl = nuspec.valueWithPath('metadata.repository@url');\n if (sourceUrl) {\n dep.sourceUrl = massageUrl(sourceUrl);\n }\n } catch (err) {\n if (isNotFound(err)) {\n shouldTryNupkg = true;\n logger.debug(\n { registryUrl, pkgName, pkgVersion: latestStable },\n `package manifest (.nuspec) not found`,\n );\n } else {\n throw err;\n }\n }\n } else {\n shouldTryNupkg = true;\n }\n\n if (!dep.sourceUrl && shouldTryNupkg && nupkgUrl) {\n const sourceUrl = await this.getSourceUrlFromNupkg(\n http,\n registryUrl,\n pkgName,\n latestStable,\n nupkgUrl,\n );\n if (sourceUrl) {\n dep.sourceUrl = massageUrl(sourceUrl);\n logger.debug(`Determined sourceUrl ${sourceUrl} from ${nupkgUrl}`);\n }\n }\n } catch (err) {\n // istanbul ignore if: not easy testable with nock\n if (err instanceof ExternalHostError) {\n throw err;\n }\n // ignore / silence 404. Seen on proget, if remote connector is used and package is not yet cached\n if (isNotFound(err)) {\n logger.debug(\n { registryUrl, pkgName, pkgVersion: latestStable },\n `package manifest (.nuspec) not found`,\n );\n } else {\n logger.debug(\n { err, registryUrl, pkgName, pkgVersion: latestStable },\n `Cannot obtain sourceUrl`,\n );\n }\n }\n\n if (homepage) {\n // only assign if not assigned\n dep.sourceUrl ??= homepage;\n dep.homepage ??= homepage;\n }\n\n return dep;\n }\n\n private async _getSourceUrlFromNupkg(\n http: Http,\n _registryUrl: string,\n packageName: string,\n packageVersion: string | null,\n nupkgUrl: string,\n ): Promise<string | null> {\n /* v8 ignore if -- specs enable RENOVATE_X_NUGET_DOWNLOAD_NUPKGS, so the disabled path is unexercised */\n if (!getEnv().RENOVATE_X_NUGET_DOWNLOAD_NUPKGS) {\n logger.once.debug('RENOVATE_X_NUGET_DOWNLOAD_NUPKGS is not set');\n return null;\n }\n const cacheDir = await ensureCacheDir('nuget');\n const nupkgFile = upath.join(\n cacheDir,\n `${packageName}.${packageVersion}.nupkg`,\n );\n const nupkgContentsDir = upath.join(\n cacheDir,\n `${packageName}.${packageVersion}`,\n );\n const readStream = http.stream(nupkgUrl);\n try {\n const writeStream = fs.createCacheWriteStream(nupkgFile);\n await fs.pipeline(readStream, writeStream);\n const zip = new AdmZip(nupkgFile);\n zip.extractAllTo(nupkgContentsDir);\n const nuspecFile = upath.join(nupkgContentsDir, `${packageName}.nuspec`);\n const nuspec = new XmlDocument(\n await fs.readCacheFile(nuspecFile, 'utf8'),\n );\n return nuspec.valueWithPath('metadata.repository@url') ?? null;\n } finally {\n await fs.rmCache(nupkgFile);\n await fs.rmCache(nupkgContentsDir);\n }\n }\n\n getSourceUrlFromNupkg(\n http: Http,\n registryUrl: string,\n packageName: string,\n packageVersion: string | null,\n nupkgUrl: string,\n ): Promise<string | null> {\n return withCache(\n {\n namespace: NugetV3Api.cacheNamespace,\n key: buildCacheKey('source-url', registryUrl, packageName),\n ttlMinutes: 10080, // 1 week\n },\n () =>\n this._getSourceUrlFromNupkg(\n http,\n registryUrl,\n packageName,\n packageVersion,\n nupkgUrl,\n ),\n );\n }\n\n getDeprecationMessage(packageName: string): string {\n return `The package \\`${packageName}\\` is deprecated.`;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA+BA,IAAa,aAAb,MAAa,WAAW;CACtB,OAAgB,iBAAiB;CAEjC,MAAM,eACJ,MACA,KACA,eAAe,wBACS;EAExB,MAAM,iBAAiB,cAAc,KAAK,YAAY;EACtD,MAAM,eAAe,MAAMA,IACzB,WAAW,gBACX,cACF;;EAGA,IAAI,cACF,OAAO;EAET,IAAI;EACJ,IAAI;GACF,MAAM,mBAAmB;GACzB,mBAAmB,MAAMA,IACvB,WAAW,gBACX,gBACF;;GAEA,IAAI,CAAC,kBAAkB;IACrB,oBACE,MAAM,KAAK,QACT,KACA,EAAE,eAAe,iBAAiB,GAClC,gBACF,EAAA,CACA;IACF,MAAMC,IACJ,WAAW,gBACX,kBACA,kBACA,IACF;GACF;GAEA,MAAM,WAAW,iBAAiB,UAC/B,KAAK,EAAE,OAAO,WAAW,SAAS,SAAS;IAC1C;IACA,MAAM,GAAG,MAAM,GAAG,CAAC,EAAE,MAAM;IAC3B,SAAS,GAAG,MAAM,GAAG,CAAC,EAAE,IAAI;GAC9B,EAAE,CAAC,CACF,QACE,EAAE,MAAM,cAAc,SAAS,gBAAgB,OAAO,MAAM,OAAO,CACtE,CAAC,CACA,MAAM,GAAG,MACR,EAAE,WAAW,EAAE,UACX,OAAO,QAAQ,EAAE,SAAS,EAAE,OAAO,6CACM,CAC/C;GAEF,IAAI,SAAS,WAAW,GAAG;IACzB,MAAMA,IACJ,WAAW,gBACX,gBACA,MACA,EACF;IACA,OAAO,MACL;KAAE;KAAK;IAAiB,GACxB,MAAM,aAAa,gBACrB;IACA,OAAO;GACT;GAEA,MAAM,EAAE,WAAW,YAAY,SAAS,IAAI;;GAG5C,IACE,iBAAiB,0BACjB,WACA,CAAC,QAAQ,WAAW,QAAQ,KAC5B,CAAC,OAAO,UAAU,SAAS,QAAQ,GAEnC,OAAO,KACL;IAAE;IAAK;GAAQ,GACf,uDACF;GAGF,MAAMA,IACJ,WAAW,gBACX,gBACA,WACA,EACF;GACA,OAAO;EACT,SAAS,KAAK;;GAEZ,IAAI,eAAe,mBACjB,MAAM;GAER,OAAO,MACL;IAAE;IAAK;IAAK;GAAiB,GAC7B,qCAAqC,cACvC;GACA,OAAO;EACT;CACF;CAEA,MAAM,gBACJ,MACA,aACyB;EACzB,IAAI,QAAQ,YAAY;EACxB,IAAI,CAAC,OAAO;GACV,MAAM,MAAM,YAAY;GACxB,IAAI,CAAC,KACH,OAAO,CAAC;GAGV,SAAQ,MADsB,KAAK,QAAQ,KAAK,WAAW,EAAA,CACnC,KAAK;EAC/B;EACA,OAAO,YAAY,KAAK,CAAC,CAAC,KAAK,EAAE,mBAAmB,YAAY;CAClE;CAEA,MAAM,YACJ,MACA,aACA,SACA,SAC+B;EAE/B,MAAM,MAAM,GADI,QAAQ,QAAQ,MAAM,MAAM,GAAG,EAC1B,EAAE,GAAG,QAAQ,YAAY,EAAE;EAEhD,MAAM,gBAAe,MADa,KAAK,QAAQ,KAAK,mBAAmB,EAAA,CAC9B,KAAK;EAC9C,MAAM,oBAAoB,aAAa,KACpC,eAAwC,KAAK,gBAAgB,MAAM,IAAI,CAC1E;EACA,MAAM,kBAAkB,MAAMC,IAAM,iBAAiB,EAAA,CAClD,KAAK,CAAC,CACN,MAAM,GAAG,MAAM,kBAAkB,EAAE,SAAS,EAAE,OAAO,CAAC;EAEzD,IAAI,WAA0B;EAC9B,IAAI,eAA8B;EAClC,IAAI,WAA0B;EAC9B,MAAM,WAAW,eAAe,KAC7B,EACC,SACA,WACA,YACA,QACA,gBACA,kBACI;GACJ,MAAM,UAAmB,EAAE,SAAS,gBAAgB,OAAO,EAAE;GAC7D,MAAM,mBAAmB,YAAY,SAAS;GAC9C,IAAI,kBACF,QAAQ,mBAAmB;GAE7B,IACEC,IAAW,QAAQ,OAAO,KAC1BA,IAAW,SAAS,OAAO,KAC3B,QACA;IACA,eAAe,gBAAgB,OAAO;IACtC,WAAW,aAAa,WAAW,UAAU,IAAI;IACjD,WAAW,WAAW,cAAc;GACtC;GAEA,IAAI,WAAW,SAAS,aACtB,QAAQ,eAAe;GAEzB,OAAO;EACT,CACF;EAEA,IAAI,CAAC,SAAS,QACZ,OAAO;;EAIT,IAAI,iBAAiB,QAAQ,aAAa,QAAQ;GAChD,MAAM,OAAO,eAAe,IAAI;GAChC,eAAe,gBAAgB,KAAK,OAAO;GAC3C,aAAa,KAAK,cAAc;GAChC,aAAa,WAAW,KAAK,cAAc;EAC7C;EAEA,MAAM,MAAqB,EACzB,SACF;EAEA,IAAI,SAAS,OAAO,YAAY,QAAQ,iBAAiB,IAAI,GAC3D,IAAI,qBAAqB,KAAK,sBAAsB,OAAO;EAG7D,IAAI;GACF,MAAM,qBAAqB,MAAM,KAAK,eACpC,MACA,aACA,oBACF;GACA,IAAI,iBAAiB;GACrB,IAAI,iBAAiB,kBAAkB,GAAG;IACxC,MAAM,YAAY,GAAG,oBACnB,kBACF,IAAI,QAAQ,YAAY,EAAE,GACxB,aACD,GAAG,QAAQ,YAAY,EAAE;IAC1B,IAAI;KACF,MAAM,aAAa,MAAM,KAAK,QAAQ,WAAW,EAC/C,eAAe,iBACjB,CAAC;KACD,MAAM,SAAS,IAAI,YAAY,WAAW,IAAI;KAC9C,MAAM,eAAe,OAAO,cAAc,uBAAuB;KACjE,IAAI,cACF,IAAI,mBAAmB;KAEzB,MAAM,YAAY,OAAO,cAAc,yBAAyB;KAChE,IAAI,WACF,IAAI,YAAY,WAAW,SAAS;IAExC,SAAS,KAAK;KACZ,IAAI,WAAW,GAAG,GAAG;MACnB,iBAAiB;MACjB,OAAO,MACL;OAAE;OAAa;OAAS,YAAY;MAAa,GACjD,sCACF;KACF,OACE,MAAM;IAEV;GACF,OACE,iBAAiB;GAGnB,IAAI,CAAC,IAAI,aAAa,kBAAkB,UAAU;IAChD,MAAM,YAAY,MAAM,KAAK,sBAC3B,MACA,aACA,SACA,cACA,QACF;IACA,IAAI,WAAW;KACb,IAAI,YAAY,WAAW,SAAS;KACpC,OAAO,MAAM,wBAAwB,UAAU,QAAQ,UAAU;IACnE;GACF;EACF,SAAS,KAAK;;GAEZ,IAAI,eAAe,mBACjB,MAAM;GAGR,IAAI,WAAW,GAAG,GAChB,OAAO,MACL;IAAE;IAAa;IAAS,YAAY;GAAa,GACjD,sCACF;QAEA,OAAO,MACL;IAAE;IAAK;IAAa;IAAS,YAAY;GAAa,GACtD,yBACF;EAEJ;EAEA,IAAI,UAAU;GAEZ,IAAI,cAAc;GAClB,IAAI,aAAa;EACnB;EAEA,OAAO;CACT;CAEA,MAAc,uBACZ,MACA,cACA,aACA,gBACA,UACwB;;EAExB,IAAI,CAAC,OAAO,CAAC,CAAC,kCAAkC;GAC9C,OAAO,KAAK,MAAM,6CAA6C;GAC/D,OAAO;EACT;EACA,MAAM,WAAW,MAAM,eAAe,OAAO;EAC7C,MAAM,YAAY,MAAM,KACtB,UACA,GAAG,YAAY,GAAG,eAAe,OACnC;EACA,MAAM,mBAAmB,MAAM,KAC7B,UACA,GAAG,YAAY,GAAG,gBACpB;EACA,MAAM,aAAa,KAAK,OAAO,QAAQ;EACvC,IAAI;GACF,MAAM,cAAcC,uBAA0B,SAAS;GACvD,MAAMC,SAAY,YAAY,WAAW;GAEzC,IADgB,OAAO,SACrB,CAAC,CAAC,aAAa,gBAAgB;GACjC,MAAM,aAAa,MAAM,KAAK,kBAAkB,GAAG,YAAY,QAAQ;GAIvE,OAAO,IAHY,YACjB,MAAMC,cAAiB,YAAY,MAAM,CAE/B,CAAC,CAAC,cAAc,yBAAyB,KAAK;EAC5D,UAAU;GACR,MAAMC,QAAW,SAAS;GAC1B,MAAMA,QAAW,gBAAgB;EACnC;CACF;CAEA,sBACE,MACA,aACA,aACA,gBACA,UACwB;EACxB,OAAO,UACL;GACE,WAAW,WAAW;GACtB,KAAK,cAAc,cAAc,aAAa,WAAW;GACzD,YAAY;EACd,SAEE,KAAK,uBACH,MACA,aACA,aACA,gBACA,QACF,CACJ;CACF;CAEA,sBAAsB,aAA6B;EACjD,OAAO,iBAAiB,YAAY;CACtC;AACF"}
@@ -1,6 +1,7 @@
1
1
  import { logger } from "../../../logger/index.js";
2
2
  import { buildCacheKey } from "../../../util/cache/package/key.js";
3
3
  import { parseUrl, replaceUrlPath, resolveBaseUrl } from "../../../util/url.js";
4
+ import { isRateLimitedOrServerError } from "../../../util/http/status.js";
4
5
  import { find } from "../../../util/host-rules.js";
5
6
  import { ExternalHostError } from "../../../types/errors/external-host-error.js";
6
7
  import { id } from "../../versioning/composer/index.js";
@@ -8,7 +9,7 @@ import { withCache } from "../../../util/cache/package/with-cache.js";
8
9
  import { Datasource } from "../datasource.js";
9
10
  import { map } from "../../../util/promises.js";
10
11
  import { PackagesResponse, PackagistFile, RegistryMeta, extractDepReleases, parsePackagesResponses } from "./schema.js";
11
- import { isNumber, isObject } from "@sindresorhus/is";
12
+ import { isObject } from "@sindresorhus/is";
12
13
  import { z } from "zod/v4";
13
14
  // lib/modules/datasource/packagist/index.ts
14
15
  function isDefaultPackagistHost(registryUrl) {
@@ -18,11 +19,9 @@ function isDefaultPackagistHost(registryUrl) {
18
19
  return ["repo.packagist.org", "packagist.org"].includes(url.host);
19
20
  }
20
21
  function isTransientPackagistError(err) {
21
- const { code: errCode, statusCode } = err;
22
+ const { code: errCode } = err;
22
23
  if (errCode && ["ECONNRESET", "ETIMEDOUT"].includes(errCode)) return true;
23
- if (!isNumber(statusCode)) return false;
24
- if (statusCode === 429 || statusCode >= 500 && statusCode < 600) return true;
25
- return false;
24
+ return isRateLimitedOrServerError(err);
26
25
  }
27
26
  var PackagistDatasource = class PackagistDatasource extends Datasource {
28
27
  static id = "packagist";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["composerVersioning.id","hostRules.find","p.map"],"sources":["../../../../lib/modules/datasource/packagist/index.ts"],"sourcesContent":["import { isNumber, isObject } from '@sindresorhus/is';\nimport { z } from 'zod/v4';\nimport { logger } from '../../../logger/index.ts';\nimport { 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 * as hostRules from '../../../util/host-rules.ts';\nimport type { HttpOptions } from '../../../util/http/types.ts';\nimport * as p from '../../../util/promises.ts';\nimport { parseUrl, replaceUrlPath, resolveBaseUrl } from '../../../util/url.ts';\nimport * as composerVersioning from '../../versioning/composer/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport type { RegistryFile } from './schema.ts';\nimport {\n PackagesResponse,\n PackagistFile,\n RegistryMeta,\n extractDepReleases,\n parsePackagesResponses,\n} from './schema.ts';\n\ninterface PackagistLookupError {\n code?: string;\n statusCode?: number;\n}\n\nfunction isDefaultPackagistHost(registryUrl: string): boolean {\n const url = parseUrl(registryUrl);\n\n /* v8 ignore if -- typescript strict null check */\n if (!url) {\n return false;\n }\n\n return ['repo.packagist.org', 'packagist.org'].includes(url.host);\n}\n\nfunction isTransientPackagistError(err: PackagistLookupError): boolean {\n const { code: errCode, statusCode } = err;\n\n if (errCode && ['ECONNRESET', 'ETIMEDOUT'].includes(errCode)) {\n return true;\n }\n\n if (!isNumber(statusCode)) {\n return false;\n }\n\n if (statusCode === 429 || (statusCode >= 500 && statusCode < 600)) {\n return true;\n }\n\n return false;\n}\n\nexport class PackagistDatasource extends Datasource {\n static readonly id = 'packagist';\n\n constructor() {\n super(PackagistDatasource.id);\n }\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return ['https://repo.packagist.org'];\n }\n\n override readonly defaultVersioning = composerVersioning.id;\n\n override readonly registryStrategy = 'hunt';\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `time` field in the results.';\n // Note: this can be changed to 'release', as the source is present in each release but we remove it while processing\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'The source URL is determined from `source` field in the results.';\n\n // We calculate auth at this datasource layer so that we can know whether it's safe to cache or not\n private static getHostOpts(url: string): HttpOptions {\n const { username, password } = hostRules.find({\n hostType: PackagistDatasource.id,\n url,\n });\n return username && password ? { username, password } : {};\n }\n\n private async getJson<Schema extends z.ZodType<any, any, any>>(\n url: string,\n schema: Schema,\n ): Promise<z.infer<Schema>> {\n const opts = PackagistDatasource.getHostOpts(url);\n const { body } = await this.http.getJson(url, opts, schema);\n return body;\n }\n\n private async _getRegistryMeta(regUrl: string): Promise<RegistryMeta> {\n const url = resolveBaseUrl(regUrl, 'packages.json');\n const result = await this.getJson(url, RegistryMeta);\n return result;\n }\n\n getRegistryMeta(regUrl: string): Promise<RegistryMeta> {\n return withCache(\n {\n namespace: `datasource-${PackagistDatasource.id}`,\n key: buildCacheKey('getRegistryMeta', regUrl),\n },\n () => this._getRegistryMeta(regUrl),\n );\n }\n\n private static isPrivatePackage(regUrl: string): boolean {\n const opts = PackagistDatasource.getHostOpts(regUrl);\n return !!opts.password;\n }\n\n private static getPackagistFileUrl(\n regUrl: string,\n regFile: RegistryFile,\n ): string {\n const { key, hash } = regFile;\n const fileName = hash\n ? key.replace('%hash%', hash)\n : /* istanbul ignore next: hard to test */ key;\n const url = resolveBaseUrl(regUrl, fileName);\n return url;\n }\n\n private async _getPackagistFile(\n regUrl: string,\n regFile: RegistryFile,\n ): Promise<PackagistFile> {\n const url = PackagistDatasource.getPackagistFileUrl(regUrl, regFile);\n const packagistFile = await this.getJson(url, PackagistFile);\n return packagistFile;\n }\n\n getPackagistFile(\n regUrl: string,\n regFile: RegistryFile,\n ): Promise<PackagistFile> {\n return withCache(\n {\n namespace: `datasource-${PackagistDatasource.id}`,\n key: buildCacheKey(\n 'getPackagistFile',\n PackagistDatasource.getPackagistFileUrl(regUrl, regFile),\n ),\n ttlMinutes: 1440,\n public: !PackagistDatasource.isPrivatePackage(regUrl),\n },\n () => this._getPackagistFile(regUrl, regFile),\n );\n }\n\n async fetchProviderPackages(\n regUrl: string,\n meta: RegistryMeta,\n ): Promise<void> {\n await p.map(meta.files, async (file) => {\n const res = await this.getPackagistFile(regUrl, file);\n Object.assign(meta.providerPackages, res.providers);\n });\n }\n\n async fetchIncludesPackages(\n regUrl: string,\n meta: RegistryMeta,\n ): Promise<void> {\n await p.map(meta.includesFiles, async (file) => {\n const res = await this.getPackagistFile(regUrl, file);\n for (const [key, val] of Object.entries(res.packages)) {\n meta.includesPackages[key] = extractDepReleases(val);\n }\n });\n }\n\n private async _packagistV2Lookup(\n registryUrl: string,\n metadataUrl: string,\n packageName: string,\n ): Promise<ReleaseResult | null> {\n const pkgUrl = replaceUrlPath(\n registryUrl,\n metadataUrl.replace('%package%', packageName),\n );\n const pkgPromise = this.getJson(pkgUrl, z.unknown());\n\n const devUrl = replaceUrlPath(\n registryUrl,\n metadataUrl.replace('%package%', `${packageName}~dev`),\n );\n const devPromise = this.getJson(devUrl, z.unknown()).then(\n (x) => x,\n () => null,\n );\n\n const responses: NonNullable<unknown>[] = await Promise.all([\n pkgPromise,\n devPromise,\n ]).then((responses) => responses.filter(isObject));\n return parsePackagesResponses(packageName, responses);\n }\n\n packagistV2Lookup(\n registryUrl: string,\n metadataUrl: string,\n packageName: string,\n ): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${PackagistDatasource.id}`,\n key: buildCacheKey(\n 'packagistV2Lookup',\n registryUrl,\n metadataUrl,\n packageName,\n ),\n ttlMinutes: 10,\n },\n () => this._packagistV2Lookup(registryUrl, metadataUrl, packageName),\n );\n }\n\n public getPkgUrl(\n packageName: string,\n registryUrl: string,\n registryMeta: RegistryMeta,\n ): string | null {\n if (\n registryMeta.providersUrl &&\n packageName in registryMeta.providerPackages\n ) {\n let url = registryMeta.providersUrl.replace('%package%', packageName);\n const hash = registryMeta.providerPackages[packageName];\n if (hash) {\n url = url.replace('%hash%', hash);\n }\n return replaceUrlPath(registryUrl, url);\n }\n\n if (registryMeta.providersLazyUrl) {\n return replaceUrlPath(\n registryUrl,\n registryMeta.providersLazyUrl.replace('%package%', packageName),\n );\n }\n\n return null;\n }\n\n public override async getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n logger.trace(`getReleases(${packageName})`);\n\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n try {\n const meta = await this.getRegistryMeta(registryUrl);\n\n if (\n meta.availablePackages &&\n !meta.availablePackages.includes(packageName)\n ) {\n return null;\n }\n\n if (meta.metadataUrl) {\n const packagistResult = await this.packagistV2Lookup(\n registryUrl,\n meta.metadataUrl,\n packageName,\n );\n return packagistResult;\n }\n\n if (meta.packages[packageName]) {\n const result = extractDepReleases(meta.packages[packageName]);\n return result;\n }\n\n await this.fetchIncludesPackages(registryUrl, meta);\n if (meta.includesPackages[packageName]) {\n return meta.includesPackages[packageName];\n }\n\n await this.fetchProviderPackages(registryUrl, meta);\n const pkgUrl = this.getPkgUrl(packageName, registryUrl, meta);\n if (!pkgUrl) {\n return null;\n }\n\n const pkgRes = await this.getJson(pkgUrl, PackagesResponse);\n const dep = extractDepReleases(pkgRes.packages[packageName]);\n logger.trace({ dep }, 'dep');\n return dep;\n } catch (err) /* istanbul ignore next */ {\n if (\n isDefaultPackagistHost(registryUrl) &&\n isTransientPackagistError(err)\n ) {\n throw new ExternalHostError(err);\n }\n throw err;\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["composerVersioning.id","hostRules.find","p.map"],"sources":["../../../../lib/modules/datasource/packagist/index.ts"],"sourcesContent":["import { isObject } from '@sindresorhus/is';\nimport { z } from 'zod/v4';\nimport { logger } from '../../../logger/index.ts';\nimport { 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 * as hostRules from '../../../util/host-rules.ts';\nimport { isRateLimitedOrServerError } from '../../../util/http/status.ts';\nimport type { HttpOptions } from '../../../util/http/types.ts';\nimport * as p from '../../../util/promises.ts';\nimport { parseUrl, replaceUrlPath, resolveBaseUrl } from '../../../util/url.ts';\nimport * as composerVersioning from '../../versioning/composer/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport type { RegistryFile } from './schema.ts';\nimport {\n PackagesResponse,\n PackagistFile,\n RegistryMeta,\n extractDepReleases,\n parsePackagesResponses,\n} from './schema.ts';\n\ninterface PackagistLookupError {\n code?: string;\n statusCode?: number;\n}\n\nfunction isDefaultPackagistHost(registryUrl: string): boolean {\n const url = parseUrl(registryUrl);\n\n /* v8 ignore if -- typescript strict null check */\n if (!url) {\n return false;\n }\n\n return ['repo.packagist.org', 'packagist.org'].includes(url.host);\n}\n\nfunction isTransientPackagistError(err: PackagistLookupError): boolean {\n const { code: errCode } = err;\n\n if (errCode && ['ECONNRESET', 'ETIMEDOUT'].includes(errCode)) {\n return true;\n }\n\n return isRateLimitedOrServerError(err);\n}\n\nexport class PackagistDatasource extends Datasource {\n static readonly id = 'packagist';\n\n constructor() {\n super(PackagistDatasource.id);\n }\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return ['https://repo.packagist.org'];\n }\n\n override readonly defaultVersioning = composerVersioning.id;\n\n override readonly registryStrategy = 'hunt';\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `time` field in the results.';\n // Note: this can be changed to 'release', as the source is present in each release but we remove it while processing\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'The source URL is determined from `source` field in the results.';\n\n // We calculate auth at this datasource layer so that we can know whether it's safe to cache or not\n private static getHostOpts(url: string): HttpOptions {\n const { username, password } = hostRules.find({\n hostType: PackagistDatasource.id,\n url,\n });\n return username && password ? { username, password } : {};\n }\n\n private async getJson<Schema extends z.ZodType<any, any, any>>(\n url: string,\n schema: Schema,\n ): Promise<z.infer<Schema>> {\n const opts = PackagistDatasource.getHostOpts(url);\n const { body } = await this.http.getJson(url, opts, schema);\n return body;\n }\n\n private async _getRegistryMeta(regUrl: string): Promise<RegistryMeta> {\n const url = resolveBaseUrl(regUrl, 'packages.json');\n const result = await this.getJson(url, RegistryMeta);\n return result;\n }\n\n getRegistryMeta(regUrl: string): Promise<RegistryMeta> {\n return withCache(\n {\n namespace: `datasource-${PackagistDatasource.id}`,\n key: buildCacheKey('getRegistryMeta', regUrl),\n },\n () => this._getRegistryMeta(regUrl),\n );\n }\n\n private static isPrivatePackage(regUrl: string): boolean {\n const opts = PackagistDatasource.getHostOpts(regUrl);\n return !!opts.password;\n }\n\n private static getPackagistFileUrl(\n regUrl: string,\n regFile: RegistryFile,\n ): string {\n const { key, hash } = regFile;\n const fileName = hash\n ? key.replace('%hash%', hash)\n : /* istanbul ignore next: hard to test */ key;\n const url = resolveBaseUrl(regUrl, fileName);\n return url;\n }\n\n private async _getPackagistFile(\n regUrl: string,\n regFile: RegistryFile,\n ): Promise<PackagistFile> {\n const url = PackagistDatasource.getPackagistFileUrl(regUrl, regFile);\n const packagistFile = await this.getJson(url, PackagistFile);\n return packagistFile;\n }\n\n getPackagistFile(\n regUrl: string,\n regFile: RegistryFile,\n ): Promise<PackagistFile> {\n return withCache(\n {\n namespace: `datasource-${PackagistDatasource.id}`,\n key: buildCacheKey(\n 'getPackagistFile',\n PackagistDatasource.getPackagistFileUrl(regUrl, regFile),\n ),\n ttlMinutes: 1440,\n public: !PackagistDatasource.isPrivatePackage(regUrl),\n },\n () => this._getPackagistFile(regUrl, regFile),\n );\n }\n\n async fetchProviderPackages(\n regUrl: string,\n meta: RegistryMeta,\n ): Promise<void> {\n await p.map(meta.files, async (file) => {\n const res = await this.getPackagistFile(regUrl, file);\n Object.assign(meta.providerPackages, res.providers);\n });\n }\n\n async fetchIncludesPackages(\n regUrl: string,\n meta: RegistryMeta,\n ): Promise<void> {\n await p.map(meta.includesFiles, async (file) => {\n const res = await this.getPackagistFile(regUrl, file);\n for (const [key, val] of Object.entries(res.packages)) {\n meta.includesPackages[key] = extractDepReleases(val);\n }\n });\n }\n\n private async _packagistV2Lookup(\n registryUrl: string,\n metadataUrl: string,\n packageName: string,\n ): Promise<ReleaseResult | null> {\n const pkgUrl = replaceUrlPath(\n registryUrl,\n metadataUrl.replace('%package%', packageName),\n );\n const pkgPromise = this.getJson(pkgUrl, z.unknown());\n\n const devUrl = replaceUrlPath(\n registryUrl,\n metadataUrl.replace('%package%', `${packageName}~dev`),\n );\n const devPromise = this.getJson(devUrl, z.unknown()).then(\n (x) => x,\n () => null,\n );\n\n const responses: NonNullable<unknown>[] = await Promise.all([\n pkgPromise,\n devPromise,\n ]).then((responses) => responses.filter(isObject));\n return parsePackagesResponses(packageName, responses);\n }\n\n packagistV2Lookup(\n registryUrl: string,\n metadataUrl: string,\n packageName: string,\n ): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${PackagistDatasource.id}`,\n key: buildCacheKey(\n 'packagistV2Lookup',\n registryUrl,\n metadataUrl,\n packageName,\n ),\n ttlMinutes: 10,\n },\n () => this._packagistV2Lookup(registryUrl, metadataUrl, packageName),\n );\n }\n\n public getPkgUrl(\n packageName: string,\n registryUrl: string,\n registryMeta: RegistryMeta,\n ): string | null {\n if (\n registryMeta.providersUrl &&\n packageName in registryMeta.providerPackages\n ) {\n let url = registryMeta.providersUrl.replace('%package%', packageName);\n const hash = registryMeta.providerPackages[packageName];\n if (hash) {\n url = url.replace('%hash%', hash);\n }\n return replaceUrlPath(registryUrl, url);\n }\n\n if (registryMeta.providersLazyUrl) {\n return replaceUrlPath(\n registryUrl,\n registryMeta.providersLazyUrl.replace('%package%', packageName),\n );\n }\n\n return null;\n }\n\n public override async getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n logger.trace(`getReleases(${packageName})`);\n\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n try {\n const meta = await this.getRegistryMeta(registryUrl);\n\n if (\n meta.availablePackages &&\n !meta.availablePackages.includes(packageName)\n ) {\n return null;\n }\n\n if (meta.metadataUrl) {\n const packagistResult = await this.packagistV2Lookup(\n registryUrl,\n meta.metadataUrl,\n packageName,\n );\n return packagistResult;\n }\n\n if (meta.packages[packageName]) {\n const result = extractDepReleases(meta.packages[packageName]);\n return result;\n }\n\n await this.fetchIncludesPackages(registryUrl, meta);\n if (meta.includesPackages[packageName]) {\n return meta.includesPackages[packageName];\n }\n\n await this.fetchProviderPackages(registryUrl, meta);\n const pkgUrl = this.getPkgUrl(packageName, registryUrl, meta);\n if (!pkgUrl) {\n return null;\n }\n\n const pkgRes = await this.getJson(pkgUrl, PackagesResponse);\n const dep = extractDepReleases(pkgRes.packages[packageName]);\n logger.trace({ dep }, 'dep');\n return dep;\n } catch (err) /* istanbul ignore next */ {\n if (\n isDefaultPackagistHost(registryUrl) &&\n isTransientPackagistError(err)\n ) {\n throw new ExternalHostError(err);\n }\n throw err;\n }\n }\n}\n"],"mappings":"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"}
@@ -2,6 +2,7 @@ import "../../../constants/error-messages.js";
2
2
  import { newlineRegex, regEx } from "../../../util/regex.js";
3
3
  import { logger } from "../../../logger/index.js";
4
4
  import { buildCacheKey } from "../../../util/cache/package/key.js";
5
+ import { isNotFound, isRateLimitedOrServerError, isUnauthorized } from "../../../util/http/status.js";
5
6
  import { ExternalHostError } from "../../../types/errors/external-host-error.js";
6
7
  import { withCache } from "../../../util/cache/package/with-cache.js";
7
8
  import { refusedHostMessage } from "../../../util/http/util.js";
@@ -27,16 +28,15 @@ function handleError(packageName, err) {
27
28
  packageName,
28
29
  err
29
30
  };
30
- const statusCode = err.response?.statusCode ?? 0;
31
- if (statusCode === 429 || statusCode >= 500 && statusCode < 600) {
31
+ if (isRateLimitedOrServerError(err)) {
32
32
  logger.warn({
33
33
  packageName,
34
34
  err
35
35
  }, `CocoaPods registry failure`);
36
36
  throw new ExternalHostError(err);
37
37
  }
38
- if (statusCode === 401) logger.debug(errorData, "Authorization error");
39
- else if (statusCode === 404) logger.debug(errorData, "Package lookup error");
38
+ if (isUnauthorized(err)) logger.debug(errorData, "Authorization error");
39
+ else if (isNotFound(err)) logger.debug(errorData, "Package lookup error");
40
40
  else if (["host-blocked", "host-disabled"].includes(err.message)) logger.trace(errorData, refusedHostMessage(err));
41
41
  else logger.warn(errorData, "CocoaPods lookup failure: Unknown error");
42
42
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../lib/modules/datasource/pod/index.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport {\n HOST_BLOCKED,\n HOST_DISABLED,\n} from '../../../constants/error-messages.ts';\nimport { logger } from '../../../logger/index.ts';\nimport { 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 { GithubHttp } from '../../../util/http/github.ts';\nimport type { HttpError } from '../../../util/http/index.ts';\nimport { refusedHostMessage } from '../../../util/http/util.ts';\nimport { newlineRegex, regEx } from '../../../util/regex.ts';\nimport { Datasource } from '../datasource.ts';\nimport { massageGithubUrl } from '../metadata.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\n\ntype URLFormatOptions =\n | 'withShardWithSpec'\n | 'withShardWithoutSpec'\n | 'withSpecsWithoutShard'\n | 'withoutSpecsWithoutShard';\n\nfunction shardParts(packageName: string): string[] {\n return crypto\n .createHash('md5')\n .update(packageName)\n .digest('hex')\n .slice(0, 3)\n .split('');\n}\n\nconst githubRegex = regEx(\n /(?<hostURL>^https:\\/\\/[a-zA-Z0-9-.]+)\\/(?<account>[^/]+)\\/(?<repo>[^/]+?)(?:\\.git|\\/.*)?$/,\n);\n\nfunction releasesGithubUrl(\n packageName: string,\n opts: {\n hostURL: string;\n account: string;\n repo: string;\n useShard: boolean;\n useSpecs: boolean;\n },\n): string {\n const { hostURL, account, repo, useShard, useSpecs } = opts;\n const prefix =\n hostURL && hostURL !== 'https://github.com'\n ? `${hostURL}/api/v3/repos`\n : 'https://api.github.com/repos';\n const shard = shardParts(packageName).join('/');\n // `Specs` in the pods repo URL is a new requirement for legacy support also allow pod repo URL without `Specs`\n const packageNamePath = useSpecs ? `Specs/${packageName}` : packageName;\n const shardPath = useSpecs\n ? `Specs/${shard}/${packageName}`\n : `${shard}/${packageName}`;\n const suffix = useShard ? shardPath : packageNamePath;\n return `${prefix}/${account}/${repo}/contents/${suffix}`;\n}\n\nfunction handleError(packageName: string, err: HttpError): void {\n const errorData = { packageName, err };\n\n const statusCode = err.response?.statusCode ?? 0;\n if (statusCode === 429 || (statusCode >= 500 && statusCode < 600)) {\n logger.warn({ packageName, err }, `CocoaPods registry failure`);\n throw new ExternalHostError(err);\n }\n\n if (statusCode === 401) {\n logger.debug(errorData, 'Authorization error');\n } else if (statusCode === 404) {\n logger.debug(errorData, 'Package lookup error');\n } else if ([HOST_BLOCKED, HOST_DISABLED].includes(err.message)) {\n logger.trace(errorData, refusedHostMessage(err));\n } else {\n logger.warn(errorData, 'CocoaPods lookup failure: Unknown error');\n }\n}\n\nfunction isDefaultRepo(url: string): boolean {\n const match = githubRegex.exec(url);\n if (match?.groups) {\n const { account, repo } = match.groups;\n return (\n account.toLowerCase() === 'cocoapods' && repo.toLowerCase() === 'specs'\n ); // https://github.com/CocoaPods/Specs.git\n }\n return false;\n}\n\nfunction releasesCDNUrl(packageName: string, registryUrl: string): string {\n const shard = shardParts(packageName).join('_');\n return `${registryUrl}/all_pods_versions_${shard}.txt`;\n}\n\nexport class PodDatasource extends Datasource {\n static readonly id = 'pod';\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return ['https://cdn.cocoapods.org'];\n }\n\n override readonly registryStrategy = 'hunt';\n\n githubHttp: GithubHttp;\n\n constructor() {\n super(PodDatasource.id);\n this.githubHttp = new GithubHttp(PodDatasource.id);\n }\n\n private async requestCDN(\n url: string,\n packageName: string,\n ): Promise<string | null> {\n try {\n const resp = await this.http.getText(url);\n if (resp?.body) {\n return resp.body;\n }\n } catch (err) {\n handleError(packageName, err);\n }\n\n return null;\n }\n\n private async requestGithub<T = unknown>(\n url: string,\n packageName: string,\n ): Promise<T | null> {\n try {\n const resp = await this.githubHttp.getJsonUnchecked<T>(url);\n // v8 ignore else -- an empty json body throws before it gets here\n if (resp?.body) {\n return resp.body;\n }\n } catch (err) {\n handleError(packageName, err);\n }\n\n return null;\n }\n\n private async getReleasesFromGithub(\n packageName: string,\n opts: { hostURL: string; account: string; repo: string },\n useShard = true,\n useSpecs = true,\n urlFormatOptions: URLFormatOptions = 'withShardWithSpec',\n ): Promise<ReleaseResult | null> {\n const url = releasesGithubUrl(packageName, { ...opts, useShard, useSpecs });\n const resp = await this.requestGithub<{ name: string }[]>(url, packageName);\n if (resp) {\n const releases = resp.map(({ name }) => ({ version: name }));\n return { releases };\n }\n\n // support different url formats\n switch (urlFormatOptions) {\n case 'withShardWithSpec':\n return this.getReleasesFromGithub(\n packageName,\n opts,\n true,\n false,\n 'withShardWithoutSpec',\n );\n case 'withShardWithoutSpec':\n return this.getReleasesFromGithub(\n packageName,\n opts,\n false,\n true,\n 'withSpecsWithoutShard',\n );\n case 'withSpecsWithoutShard':\n return this.getReleasesFromGithub(\n packageName,\n opts,\n false,\n false,\n 'withoutSpecsWithoutShard',\n );\n case 'withoutSpecsWithoutShard':\n default:\n return null;\n }\n }\n\n private async getReleasesFromCDN(\n packageName: string,\n registryUrl: string,\n ): Promise<ReleaseResult | null> {\n const url = releasesCDNUrl(packageName, registryUrl);\n const resp = await this.requestCDN(url, packageName);\n if (resp) {\n const lines = resp.split(newlineRegex);\n for (const line of lines) {\n const [name, ...versions] = line.split('/');\n if (name === packageName.replace(regEx(/\\/.*$/), '')) {\n const releases = versions.map((version) => ({ version }));\n return { releases };\n }\n }\n }\n return null;\n }\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 const podName = packageName.replace(regEx(/\\/.*$/), '');\n let baseUrl = registryUrl.replace(regEx(/\\/+$/), '');\n // In order to not abuse github API limits, query CDN instead\n if (isDefaultRepo(baseUrl)) {\n [baseUrl] = this.getDefaultRegistryUrls('');\n }\n\n let result: ReleaseResult | null = null;\n const match = githubRegex.exec(baseUrl);\n // We would ideally have a reliable way to differentiate between\n // a CDN URL and a Github URL, but we'll start with detecting Artifactory\n if (match?.groups && !baseUrl.includes('/api/pods/')) {\n baseUrl = massageGithubUrl(baseUrl);\n const { hostURL, account, repo } = match.groups;\n const opts = { hostURL, account, repo };\n result = await this.getReleasesFromGithub(podName, opts);\n } else {\n result = await this.getReleasesFromCDN(podName, baseUrl);\n }\n\n return result;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n ttlMinutes: 30,\n namespace: `datasource-${PodDatasource.id}`,\n key: buildCacheKey(config.registryUrl, config.packageName),\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;AAuBA,SAAS,WAAW,aAA+B;CACjD,OAAO,OACJ,WAAW,KAAK,CAAC,CACjB,OAAO,WAAW,CAAC,CACnB,OAAO,KAAK,CAAC,CACb,MAAM,GAAG,CAAC,CAAC,CACX,MAAM,EAAE;AACb;AAEA,MAAM,cAAc,MAClB,2FACF;AAEA,SAAS,kBACP,aACA,MAOQ;CACR,MAAM,EAAE,SAAS,SAAS,MAAM,UAAU,aAAa;CACvD,MAAM,SACJ,WAAW,YAAY,uBACnB,GAAG,QAAQ,iBACX;CACN,MAAM,QAAQ,WAAW,WAAW,CAAC,CAAC,KAAK,GAAG;CAE9C,MAAM,kBAAkB,WAAW,SAAS,gBAAgB;CAC5D,MAAM,YAAY,WACd,SAAS,MAAM,GAAG,gBAClB,GAAG,MAAM,GAAG;CAEhB,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,YADrB,WAAW,YAAY;AAExC;AAEA,SAAS,YAAY,aAAqB,KAAsB;CAC9D,MAAM,YAAY;EAAE;EAAa;CAAI;CAErC,MAAM,aAAa,IAAI,UAAU,cAAc;CAC/C,IAAI,eAAe,OAAQ,cAAc,OAAO,aAAa,KAAM;EACjE,OAAO,KAAK;GAAE;GAAa;EAAI,GAAG,4BAA4B;EAC9D,MAAM,IAAI,kBAAkB,GAAG;CACjC;CAEA,IAAI,eAAe,KACjB,OAAO,MAAM,WAAW,qBAAqB;MACxC,IAAI,eAAe,KACxB,OAAO,MAAM,WAAW,sBAAsB;MACzC,IAAI,CAAA,gBAAA,eAA4B,CAAC,CAAC,SAAS,IAAI,OAAO,GAC3D,OAAO,MAAM,WAAW,mBAAmB,GAAG,CAAC;MAE/C,OAAO,KAAK,WAAW,yCAAyC;AAEpE;AAEA,SAAS,cAAc,KAAsB;CAC3C,MAAM,QAAQ,YAAY,KAAK,GAAG;CAClC,IAAI,OAAO,QAAQ;EACjB,MAAM,EAAE,SAAS,SAAS,MAAM;EAChC,OACE,QAAQ,YAAY,MAAM,eAAe,KAAK,YAAY,MAAM;CAEpE;CACA,OAAO;AACT;AAEA,SAAS,eAAe,aAAqB,aAA6B;CAExE,OAAO,GAAG,YAAY,qBADR,WAAW,WAAW,CAAC,CAAC,KAAK,GACI,EAAE;AACnD;AAEA,IAAa,gBAAb,MAAa,sBAAsB,WAAW;CAC5C,OAAgB,KAAK;CAErB,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,2BAA2B;CACrC;CAEA,mBAAqC;CAErC;CAEA,cAAc;EACZ,MAAM,cAAc,EAAE;EACtB,KAAK,aAAa,IAAI,WAAW,cAAc,EAAE;CACnD;CAEA,MAAc,WACZ,KACA,aACwB;EACxB,IAAI;GACF,MAAM,OAAO,MAAM,KAAK,KAAK,QAAQ,GAAG;GACxC,IAAI,MAAM,MACR,OAAO,KAAK;EAEhB,SAAS,KAAK;GACZ,YAAY,aAAa,GAAG;EAC9B;EAEA,OAAO;CACT;CAEA,MAAc,cACZ,KACA,aACmB;EACnB,IAAI;GACF,MAAM,OAAO,MAAM,KAAK,WAAW,iBAAoB,GAAG;;GAE1D,IAAI,MAAM,MACR,OAAO,KAAK;EAEhB,SAAS,KAAK;GACZ,YAAY,aAAa,GAAG;EAC9B;EAEA,OAAO;CACT;CAEA,MAAc,sBACZ,aACA,MACA,WAAW,MACX,WAAW,MACX,mBAAqC,qBACN;EAC/B,MAAM,MAAM,kBAAkB,aAAa;GAAE,GAAG;GAAM;GAAU;EAAS,CAAC;EAC1E,MAAM,OAAO,MAAM,KAAK,cAAkC,KAAK,WAAW;EAC1E,IAAI,MAEF,OAAO,EAAE,UADQ,KAAK,KAAK,EAAE,YAAY,EAAE,SAAS,KAAK,EACzC,EAAE;EAIpB,QAAQ,kBAAR;GACE,KAAK,qBACH,OAAO,KAAK,sBACV,aACA,MACA,MACA,OACA,sBACF;GACF,KAAK,wBACH,OAAO,KAAK,sBACV,aACA,MACA,OACA,MACA,uBACF;GACF,KAAK,yBACH,OAAO,KAAK,sBACV,aACA,MACA,OACA,OACA,0BACF;GAEF,SACE,OAAO;EACX;CACF;CAEA,MAAc,mBACZ,aACA,aAC+B;EAC/B,MAAM,MAAM,eAAe,aAAa,WAAW;EACnD,MAAM,OAAO,MAAM,KAAK,WAAW,KAAK,WAAW;EACnD,IAAI,MAAM;GACR,MAAM,QAAQ,KAAK,MAAM,YAAY;GACrC,KAAK,MAAM,QAAQ,OAAO;IACxB,MAAM,CAAC,MAAM,GAAG,YAAY,KAAK,MAAM,GAAG;IAC1C,IAAI,SAAS,YAAY,QAAQ,MAAM,OAAO,GAAG,EAAE,GAEjD,OAAO,EAAE,UADQ,SAAS,KAAK,aAAa,EAAE,QAAQ,EACtC,EAAE;GAEtB;EACF;EACA,OAAO;CACT;CAEA,MAAc,aAAa,EACzB,aACA,eACmD;;EAEnD,IAAI,CAAC,aACH,OAAO;EAGT,MAAM,UAAU,YAAY,QAAQ,MAAM,OAAO,GAAG,EAAE;EACtD,IAAI,UAAU,YAAY,QAAQ,MAAM,MAAM,GAAG,EAAE;EAEnD,IAAI,cAAc,OAAO,GACvB,CAAC,WAAW,KAAK,uBAAuB,EAAE;EAG5C,IAAI,SAA+B;EACnC,MAAM,QAAQ,YAAY,KAAK,OAAO;EAGtC,IAAI,OAAO,UAAU,CAAC,QAAQ,SAAS,YAAY,GAAG;GACpD,UAAU,iBAAiB,OAAO;GAClC,MAAM,EAAE,SAAS,SAAS,SAAS,MAAM;GACzC,MAAM,OAAO;IAAE;IAAS;IAAS;GAAK;GACtC,SAAS,MAAM,KAAK,sBAAsB,SAAS,IAAI;EACzD,OACE,SAAS,MAAM,KAAK,mBAAmB,SAAS,OAAO;EAGzD,OAAO;CACT;CAEA,YAAY,QAA0D;EACpE,OAAO,UACL;GACE,YAAY;GACZ,WAAW,cAAc,cAAc;GACvC,KAAK,cAAc,OAAO,aAAa,OAAO,WAAW;GACzD,UAAU;EACZ,SACM,KAAK,aAAa,MAAM,CAChC;CACF;AACF"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../lib/modules/datasource/pod/index.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport {\n HOST_BLOCKED,\n HOST_DISABLED,\n} from '../../../constants/error-messages.ts';\nimport { logger } from '../../../logger/index.ts';\nimport { 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 { GithubHttp } from '../../../util/http/github.ts';\nimport type { HttpError } from '../../../util/http/index.ts';\nimport {\n isNotFound,\n isRateLimitedOrServerError,\n isUnauthorized,\n} from '../../../util/http/status.ts';\nimport { refusedHostMessage } from '../../../util/http/util.ts';\nimport { newlineRegex, regEx } from '../../../util/regex.ts';\nimport { Datasource } from '../datasource.ts';\nimport { massageGithubUrl } from '../metadata.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\n\ntype URLFormatOptions =\n | 'withShardWithSpec'\n | 'withShardWithoutSpec'\n | 'withSpecsWithoutShard'\n | 'withoutSpecsWithoutShard';\n\nfunction shardParts(packageName: string): string[] {\n return crypto\n .createHash('md5')\n .update(packageName)\n .digest('hex')\n .slice(0, 3)\n .split('');\n}\n\nconst githubRegex = regEx(\n /(?<hostURL>^https:\\/\\/[a-zA-Z0-9-.]+)\\/(?<account>[^/]+)\\/(?<repo>[^/]+?)(?:\\.git|\\/.*)?$/,\n);\n\nfunction releasesGithubUrl(\n packageName: string,\n opts: {\n hostURL: string;\n account: string;\n repo: string;\n useShard: boolean;\n useSpecs: boolean;\n },\n): string {\n const { hostURL, account, repo, useShard, useSpecs } = opts;\n const prefix =\n hostURL && hostURL !== 'https://github.com'\n ? `${hostURL}/api/v3/repos`\n : 'https://api.github.com/repos';\n const shard = shardParts(packageName).join('/');\n // `Specs` in the pods repo URL is a new requirement for legacy support also allow pod repo URL without `Specs`\n const packageNamePath = useSpecs ? `Specs/${packageName}` : packageName;\n const shardPath = useSpecs\n ? `Specs/${shard}/${packageName}`\n : `${shard}/${packageName}`;\n const suffix = useShard ? shardPath : packageNamePath;\n return `${prefix}/${account}/${repo}/contents/${suffix}`;\n}\n\nfunction handleError(packageName: string, err: HttpError): void {\n const errorData = { packageName, err };\n\n if (isRateLimitedOrServerError(err)) {\n logger.warn({ packageName, err }, `CocoaPods registry failure`);\n throw new ExternalHostError(err);\n }\n\n if (isUnauthorized(err)) {\n logger.debug(errorData, 'Authorization error');\n } else if (isNotFound(err)) {\n logger.debug(errorData, 'Package lookup error');\n } else if ([HOST_BLOCKED, HOST_DISABLED].includes(err.message)) {\n logger.trace(errorData, refusedHostMessage(err));\n } else {\n logger.warn(errorData, 'CocoaPods lookup failure: Unknown error');\n }\n}\n\nfunction isDefaultRepo(url: string): boolean {\n const match = githubRegex.exec(url);\n if (match?.groups) {\n const { account, repo } = match.groups;\n return (\n account.toLowerCase() === 'cocoapods' && repo.toLowerCase() === 'specs'\n ); // https://github.com/CocoaPods/Specs.git\n }\n return false;\n}\n\nfunction releasesCDNUrl(packageName: string, registryUrl: string): string {\n const shard = shardParts(packageName).join('_');\n return `${registryUrl}/all_pods_versions_${shard}.txt`;\n}\n\nexport class PodDatasource extends Datasource {\n static readonly id = 'pod';\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return ['https://cdn.cocoapods.org'];\n }\n\n override readonly registryStrategy = 'hunt';\n\n githubHttp: GithubHttp;\n\n constructor() {\n super(PodDatasource.id);\n this.githubHttp = new GithubHttp(PodDatasource.id);\n }\n\n private async requestCDN(\n url: string,\n packageName: string,\n ): Promise<string | null> {\n try {\n const resp = await this.http.getText(url);\n if (resp?.body) {\n return resp.body;\n }\n } catch (err) {\n handleError(packageName, err);\n }\n\n return null;\n }\n\n private async requestGithub<T = unknown>(\n url: string,\n packageName: string,\n ): Promise<T | null> {\n try {\n const resp = await this.githubHttp.getJsonUnchecked<T>(url);\n // v8 ignore else -- an empty json body throws before it gets here\n if (resp?.body) {\n return resp.body;\n }\n } catch (err) {\n handleError(packageName, err);\n }\n\n return null;\n }\n\n private async getReleasesFromGithub(\n packageName: string,\n opts: { hostURL: string; account: string; repo: string },\n useShard = true,\n useSpecs = true,\n urlFormatOptions: URLFormatOptions = 'withShardWithSpec',\n ): Promise<ReleaseResult | null> {\n const url = releasesGithubUrl(packageName, { ...opts, useShard, useSpecs });\n const resp = await this.requestGithub<{ name: string }[]>(url, packageName);\n if (resp) {\n const releases = resp.map(({ name }) => ({ version: name }));\n return { releases };\n }\n\n // support different url formats\n switch (urlFormatOptions) {\n case 'withShardWithSpec':\n return this.getReleasesFromGithub(\n packageName,\n opts,\n true,\n false,\n 'withShardWithoutSpec',\n );\n case 'withShardWithoutSpec':\n return this.getReleasesFromGithub(\n packageName,\n opts,\n false,\n true,\n 'withSpecsWithoutShard',\n );\n case 'withSpecsWithoutShard':\n return this.getReleasesFromGithub(\n packageName,\n opts,\n false,\n false,\n 'withoutSpecsWithoutShard',\n );\n case 'withoutSpecsWithoutShard':\n default:\n return null;\n }\n }\n\n private async getReleasesFromCDN(\n packageName: string,\n registryUrl: string,\n ): Promise<ReleaseResult | null> {\n const url = releasesCDNUrl(packageName, registryUrl);\n const resp = await this.requestCDN(url, packageName);\n if (resp) {\n const lines = resp.split(newlineRegex);\n for (const line of lines) {\n const [name, ...versions] = line.split('/');\n if (name === packageName.replace(regEx(/\\/.*$/), '')) {\n const releases = versions.map((version) => ({ version }));\n return { releases };\n }\n }\n }\n return null;\n }\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 const podName = packageName.replace(regEx(/\\/.*$/), '');\n let baseUrl = registryUrl.replace(regEx(/\\/+$/), '');\n // In order to not abuse github API limits, query CDN instead\n if (isDefaultRepo(baseUrl)) {\n [baseUrl] = this.getDefaultRegistryUrls('');\n }\n\n let result: ReleaseResult | null = null;\n const match = githubRegex.exec(baseUrl);\n // We would ideally have a reliable way to differentiate between\n // a CDN URL and a Github URL, but we'll start with detecting Artifactory\n if (match?.groups && !baseUrl.includes('/api/pods/')) {\n baseUrl = massageGithubUrl(baseUrl);\n const { hostURL, account, repo } = match.groups;\n const opts = { hostURL, account, repo };\n result = await this.getReleasesFromGithub(podName, opts);\n } else {\n result = await this.getReleasesFromCDN(podName, baseUrl);\n }\n\n return result;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n ttlMinutes: 30,\n namespace: `datasource-${PodDatasource.id}`,\n key: buildCacheKey(config.registryUrl, config.packageName),\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n}\n"],"mappings":"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"}
@@ -5,6 +5,7 @@ import { toBase64 } from "../../../util/string.js";
5
5
  import { addSecretForSanitizing } from "../../../util/sanitize.js";
6
6
  import { logger } from "../../../logger/index.js";
7
7
  import { ensureTrailingSlash, parseUrl } from "../../../util/url.js";
8
+ import { isNotAcceptable } from "../../../util/http/status.js";
8
9
  import { ExternalHostError } from "../../../types/errors/external-host-error.js";
9
10
  import { Json } from "../../../util/schema-utils/index.js";
10
11
  import { id } from "../../versioning/pep440/index.js";
@@ -20,6 +21,19 @@ import { PypiResponse, PypiSimpleResponse } from "./schema.js";
20
21
  import { isNonEmptyString } from "@sindresorhus/is";
21
22
  import changelogFilenameRegex from "changelog-filename-regex";
22
23
  // lib/modules/datasource/pypi/index.ts
24
+ function isSourceLabel(lower, projectUrl) {
25
+ return lower.startsWith("repo") || lower === "code" || lower === "source" || isGitHubRepo(projectUrl);
26
+ }
27
+ function isChangelogLabel(lower) {
28
+ return [
29
+ "changelog",
30
+ "change log",
31
+ "changes",
32
+ "release notes",
33
+ "news",
34
+ "what's new"
35
+ ].includes(lower) || !!changelogFilenameRegex.exec(lower);
36
+ }
23
37
  var PypiDatasource = class PypiDatasource extends Datasource {
24
38
  static id = pypiDatasourceId;
25
39
  constructor() {
@@ -114,15 +128,8 @@ var PypiDatasource = class PypiDatasource extends Datasource {
114
128
  }
115
129
  if (dep.info?.project_urls) for (const [name, projectUrl] of Object.entries(dep.info.project_urls)) {
116
130
  const lower = name.toLowerCase();
117
- if (projectUrl && !dependency.sourceUrl && (lower.startsWith("repo") || lower === "code" || lower === "source" || isGitHubRepo(projectUrl))) dependency.sourceUrl = projectUrl;
118
- if (!dependency.changelogUrl && ([
119
- "changelog",
120
- "change log",
121
- "changes",
122
- "release notes",
123
- "news",
124
- "what's new"
125
- ].includes(lower) || changelogFilenameRegex.exec(lower))) dependency.changelogUrl = projectUrl;
131
+ if (projectUrl && !dependency.sourceUrl && isSourceLabel(lower, projectUrl)) dependency.sourceUrl = projectUrl;
132
+ if (!dependency.changelogUrl && isChangelogLabel(lower)) dependency.changelogUrl = projectUrl;
126
133
  }
127
134
  if (dep.releases) dependency.releases = PypiDatasource.toReleases(dep.releases);
128
135
  return dependency;
@@ -131,7 +138,8 @@ var PypiDatasource = class PypiDatasource extends Datasource {
131
138
  let earliest = null;
132
139
  for (const { upload_time } of releases) {
133
140
  const timestamp = asTimestamp(upload_time);
134
- if (timestamp && (!earliest || timestamp < earliest)) earliest = timestamp;
141
+ if (!timestamp) continue;
142
+ if (!earliest || timestamp < earliest) earliest = timestamp;
135
143
  }
136
144
  return earliest;
137
145
  }
@@ -200,7 +208,7 @@ var PypiDatasource = class PypiDatasource extends Datasource {
200
208
  } catch (err) {
201
209
  const httpErr = err instanceof ExternalHostError ? err.err : err;
202
210
  const statusCode = httpErr instanceof RequestError ? httpErr.response?.statusCode : void 0;
203
- if (statusCode !== 406) throw err;
211
+ if (!isNotAcceptable(statusCode)) throw err;
204
212
  logger.trace({
205
213
  packageName,
206
214
  hostUrl,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["pep440.id","HttpError"],"sources":["../../../../lib/modules/datasource/pypi/index.ts"],"sourcesContent":["import { isNonEmptyString } from '@sindresorhus/is';\nimport changelogFilenameRegex from 'changelog-filename-regex';\nimport { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { coerceArray, deduplicateArray } from '../../../util/array.ts';\nimport { getEnv } from '../../../util/env.ts';\nimport { parse } from '../../../util/html.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport type {\n HttpResponse,\n OutgoingHttpHeaders,\n} from '../../../util/http/types.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { addSecretForSanitizing } from '../../../util/sanitize.ts';\nimport { Json } from '../../../util/schema-utils/index.ts';\nimport { toBase64 } from '../../../util/string.ts';\nimport type { Timestamp } from '../../../util/timestamp.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport { ensureTrailingSlash, parseUrl } from '../../../util/url.ts';\nimport * as pep440 from '../../versioning/pep440/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, Release, ReleaseResult } from '../types.ts';\nimport { isGoogleArtifactRegistry } from '../util.ts';\nimport {\n isGitHubRepo,\n normalizePythonDepName,\n pypiDatasourceId,\n} from './common.ts';\nimport { findPypiIndexCredentials } from './host-rules.ts';\nimport type { PypiRelease } from './schema.ts';\nimport { PypiResponse, PypiSimpleResponse } from './schema.ts';\nimport type { Releases } from './types.ts';\n\nexport class PypiDatasource extends Datasource {\n static readonly id = pypiDatasourceId;\n\n constructor() {\n super(PypiDatasource.id);\n }\n\n override readonly caching = true;\n\n override supportsCustomRegistry(_packageName: string): boolean {\n return true;\n }\n\n static readonly defaultURL =\n getEnv().PIP_INDEX_URL ?? 'https://pypi.org/pypi/';\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return [PypiDatasource.defaultURL];\n }\n\n override readonly defaultVersioning = pep440.id;\n\n override readonly registryStrategy = 'merge';\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the earliest `upload_time` field of the files of a version. When using the Simple API, timestamps are available if the server supports the JSON-based Simple API (PEP 691).';\n override readonly sourceUrlSupport = 'release';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `homepage` field if it is a github repository, else we use the `project_urls` field.';\n\n async getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n let dependency: ReleaseResult | null = null;\n // TODO: null check (#22198)\n const hostUrl = ensureTrailingSlash(\n registryUrl!.replace('https://pypi.org/simple', 'https://pypi.org/pypi'),\n );\n const normalizedLookupName = normalizePythonDepName(packageName);\n\n // not all simple indexes use this identifier, but most do\n if (hostUrl.endsWith('/simple/') || hostUrl.endsWith('/+simple/')) {\n logger.trace(\n { packageName, hostUrl },\n 'Looking up pypi simple dependency',\n );\n dependency = await this.getSimpleDependency(\n normalizedLookupName,\n hostUrl,\n );\n } else {\n logger.trace({ packageName, hostUrl }, 'Looking up pypi api dependency');\n try {\n // we need to resolve early here so we can catch any 404s and fallback to a simple lookup\n dependency = await this.getDependency(normalizedLookupName, hostUrl);\n } catch (err) {\n // error contacting json-style api -- attempt to fallback to a simple-style api\n logger.trace(\n { packageName, hostUrl, err },\n 'Looking up pypi simple dependency via fallback',\n );\n dependency = await this.getSimpleDependency(\n normalizedLookupName,\n hostUrl,\n );\n }\n }\n return dependency;\n }\n\n private sanitizeLookupUrl(lookupUrl: string, parsedUrl: URL): string {\n if (!parsedUrl.username && !parsedUrl.password) {\n return lookupUrl;\n }\n\n parsedUrl.username = '';\n parsedUrl.password = '';\n return parsedUrl.toString();\n }\n\n private async getAuthHeaders(\n lookupUrl: string,\n ): Promise<{ headers: OutgoingHttpHeaders; lookupUrl: string }> {\n const parsedUrl = parseUrl(lookupUrl);\n // v8 ignore if -- TODO: refactor to cover this branch through public behavior again\n if (!parsedUrl) {\n logger.once.debug({ lookupUrl }, 'Failed to parse URL');\n return { headers: {}, lookupUrl };\n }\n // Only Google Artifact Registry needs a header of our own: for every other host `applyHostRule` already turns the matching host rule into request auth\n if (!isGoogleArtifactRegistry(parsedUrl.hostname)) {\n return { headers: {}, lookupUrl };\n }\n\n const { username, password } = await findPypiIndexCredentials(lookupUrl);\n if (!username && !password) {\n return { headers: {}, lookupUrl };\n }\n\n const auth = toBase64(`${username ?? ''}:${password ?? ''}`);\n addSecretForSanitizing(auth);\n return {\n headers: { authorization: `Basic ${auth}` },\n lookupUrl: this.sanitizeLookupUrl(lookupUrl, parsedUrl),\n };\n }\n\n private async getDependency(\n packageName: string,\n hostUrl: string,\n ): Promise<ReleaseResult | null> {\n const lookupUrl = new URL(\n `${normalizePythonDepName(packageName)}/json`,\n hostUrl,\n ).href;\n const dependency: ReleaseResult = { releases: [] };\n logger.trace({ lookupUrl }, 'Pypi api got lookup');\n const { headers, lookupUrl: sanitizedUrl } =\n await this.getAuthHeaders(lookupUrl);\n const rep = await this.http.getJson(\n sanitizedUrl,\n { headers },\n PypiResponse,\n );\n const dep = rep?.body;\n if (rep.authorization) {\n dependency.isPrivate = true;\n }\n logger.trace({ lookupUrl }, 'Got pypi api result');\n\n if (dep.info?.home_page) {\n dependency.homepage = dep.info.home_page;\n if (isGitHubRepo(dep.info.home_page)) {\n dependency.sourceUrl = dep.info.home_page.replace(\n 'http://',\n 'https://',\n );\n }\n }\n\n if (dep.info?.project_urls) {\n for (const [name, projectUrl] of Object.entries(dep.info.project_urls)) {\n const lower = name.toLowerCase();\n\n if (\n projectUrl &&\n !dependency.sourceUrl &&\n (lower.startsWith('repo') ||\n lower === 'code' ||\n lower === 'source' ||\n isGitHubRepo(projectUrl))\n ) {\n dependency.sourceUrl = projectUrl;\n }\n\n if (\n !dependency.changelogUrl &&\n ([\n 'changelog',\n 'change log',\n 'changes',\n 'release notes',\n 'news',\n \"what's new\",\n ].includes(lower) ||\n changelogFilenameRegex.exec(lower))\n ) {\n // from https://github.com/pypa/warehouse/blob/418c7511dc367fb410c71be139545d0134ccb0df/warehouse/templates/packaging/detail.html#L24\n dependency.changelogUrl = projectUrl;\n }\n }\n }\n\n if (dep.releases) {\n dependency.releases = PypiDatasource.toReleases(dep.releases);\n }\n return dependency;\n }\n\n private static getEarliestTimestamp(\n releases: PypiRelease[],\n ): Timestamp | null {\n let earliest: Timestamp | null = null;\n for (const { upload_time } of releases) {\n const timestamp = asTimestamp(upload_time);\n // `asTimestamp` normalizes to UTC ISO 8601, so comparing the strings compares the instants\n if (timestamp && (!earliest || timestamp < earliest)) {\n earliest = timestamp;\n }\n }\n return earliest;\n }\n\n private static extractVersionFromLinkText(\n text: string,\n packageName: string,\n ): string | null {\n // source packages\n const lcText = text.toLowerCase();\n const normalizedSrcText = normalizePythonDepName(text);\n const srcPrefix = `${packageName}-`;\n\n // source distribution format: `{name}-{version}.tar.gz` (https://packaging.python.org/en/latest/specifications/source-distribution-format/#source-distribution-file-name)\n // binary distribution: `{distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl` (https://packaging.python.org/en/latest/specifications/binary-distribution-format/#file-name-convention)\n // officially both `name` and `distribution` should be normalized and then the - replaced with _, but in reality this is not the case\n // We therefore normalize the name we have (replacing `_-.` with -) and then check if the text starts with the normalized name\n\n if (!normalizedSrcText.startsWith(srcPrefix)) {\n return null;\n }\n\n // strip off the prefix using the prefix length as we may have normalized the srcPrefix/packageName\n // We assume that neither the version nor the suffix contains multiple `-` like `0.1.2---rc1.tar.gz`\n // and use the difference in length to strip off the prefix in case the name contains double `--` characters\n const normalizedLengthDiff = lcText.length - normalizedSrcText.length;\n const res = lcText.slice(srcPrefix.length + normalizedLengthDiff);\n\n // source distribution\n const srcSuffixes = ['.tar.gz', '.tar.bz2', '.tar.xz', '.zip', '.tgz'];\n const srcSuffix = srcSuffixes.find((suffix) => lcText.endsWith(suffix));\n if (srcSuffix) {\n // strip off the suffix using character length\n return res.slice(0, -srcSuffix.length);\n }\n\n // binary distribution\n // for binary distributions the version is the first part after the removed distribution name\n const wheelSuffix = '.whl';\n if (lcText.endsWith(wheelSuffix) && lcText.split('-').length > 2) {\n return res.split('-')[0];\n }\n return null;\n }\n\n private static cleanSimpleHtml(html: string): string {\n return (\n html\n .replace(regEx(/<\\/?pre>/), '')\n // Certain simple repositories like artifactory don't escape > and <\n .replace(\n regEx(/data-requires-python=\"(?<before>[^\"]*?)>(?<after>[^\"]*?)\"/g),\n 'data-requires-python=\"$<before>&gt;$<after>\"',\n )\n .replace(\n regEx(/data-requires-python=\"(?<before>[^\"]*?)<(?<after>[^\"]*?)\"/g),\n 'data-requires-python=\"$<before>&lt;$<after>\"',\n )\n );\n }\n\n private static getSimpleReleasesFromHtml(\n html: string,\n packageName: string,\n ): Releases {\n const root = parse(PypiDatasource.cleanSimpleHtml(html));\n const links = root.querySelectorAll('a');\n const releases: Releases = {};\n for (const link of Array.from(links)) {\n const version = PypiDatasource.extractVersionFromLinkText(\n link.text?.trim(),\n packageName,\n );\n if (version) {\n const release: PypiRelease = {\n yanked: link.hasAttribute('data-yanked'),\n };\n const requiresPython = link.getAttribute('data-requires-python');\n if (requiresPython) {\n release.requires_python = requiresPython;\n }\n (releases[version] ??= []).push(release);\n }\n }\n return releases;\n }\n\n private static getSimpleReleasesFromJson(\n json: string,\n packageName: string,\n ): Releases | null {\n const releases: Releases = {};\n const parsed = Json.pipe(PypiSimpleResponse).safeParse(json);\n if (!parsed.success) {\n logger.once.warn(\n { packageName, err: parsed.error },\n 'Failed to parse JSON-based Simple API response',\n );\n // Distinguish a malformed response from a package with genuinely no files, so the caller doesn't mistake an error for an empty result.\n return null;\n }\n for (const file of parsed.data.files) {\n const version = PypiDatasource.extractVersionFromLinkText(\n file.filename,\n packageName,\n );\n if (version) {\n (releases[version] ??= []).push(file);\n }\n }\n return releases;\n }\n\n private async getSimpleDependency(\n packageName: string,\n hostUrl: string,\n ): Promise<ReleaseResult | null> {\n const lookupUrl = new URL(\n ensureTrailingSlash(normalizePythonDepName(packageName)),\n hostUrl,\n ).href;\n const dependency: ReleaseResult = { releases: [] };\n const { headers: authHeaders, lookupUrl: sanitizedUrl } =\n await this.getAuthHeaders(lookupUrl);\n const headers: OutgoingHttpHeaders = {\n ...authHeaders,\n // Request the JSON serialization (PEP 691), falling back to the legacy HTML serialization (PEP 503) via content negotiation.\n // https://github.com/pypa/pip/blob/6b0011b49a068c62f65389bf4cea7af5d28cb002/src/pip/_internal/index/collector.py#L128-L134\n accept:\n 'application/vnd.pypi.simple.v1+json, application/vnd.pypi.simple.v1+html; q=0.1, text/html; q=0.01',\n };\n let response: HttpResponse;\n try {\n response = await this.http.getText(sanitizedUrl, { headers });\n } catch (err) {\n // An `abortOnError` host rule makes `Http` wrap the original error, so unwrap it before looking at the status code.\n const httpErr = err instanceof ExternalHostError ? err.err : err;\n const statusCode =\n httpErr instanceof HttpError ? httpErr.response?.statusCode : undefined;\n // A registry which cannot serve any of the negotiated types may pick a content type of its own, answer `406`, or answer `300`; the spec mandates none of them, but `406` is the common choice.\n // https://packaging.python.org/en/latest/specifications/simple-repository-api/#version-format-selection\n // Retry once without the negotiated `accept` header to preserve the pre-PEP-691 behaviour of a plain request.\n if (statusCode !== 406) {\n throw err;\n }\n\n logger.trace(\n { packageName, hostUrl, statusCode },\n 'Registry rejected negotiated Accept header, retrying without it',\n );\n response = await this.http.getText(sanitizedUrl, {\n headers: authHeaders,\n });\n }\n const dep = response?.body;\n if (!dep) {\n logger.trace({ dependency: packageName }, 'pip package not found');\n return null;\n }\n if (response.authorization) {\n dependency.isPrivate = true;\n }\n\n // Dispatch on the response content-type: JSON serialization (PEP 691) or the legacy HTML serialization (PEP 503). Matched loosely (any `json` media type) rather than the exact vendor type, as some registries/proxies relabel or strip the vendor-specific media type while still returning JSON.\n const contentType = response.headers['content-type'];\n const isJson = !!contentType && contentType.includes('json');\n const looksLikeJson = dep.trimStart().startsWith('{');\n\n if (isJson) {\n if (looksLikeJson) {\n const releases = PypiDatasource.getSimpleReleasesFromJson(\n dep,\n packageName,\n );\n // A parse failure is distinct from a package with genuinely no files, so don't report an empty release list as if it were a real (if empty) result, which would be cached as such.\n if (!releases) {\n return null;\n }\n dependency.releases = PypiDatasource.toReleases(releases);\n return dependency;\n }\n\n // The registry mislabeled a non-JSON body as JSON, so fall through to the HTML parser instead of reporting the package as not found.\n logger.debug(\n { packageName, hostUrl, contentType },\n 'Parsing Simple API response as HTML, as it is labeled as JSON but does not look like it',\n );\n }\n\n const htmlReleases = PypiDatasource.getSimpleReleasesFromHtml(\n dep,\n packageName,\n );\n dependency.releases = PypiDatasource.toReleases(htmlReleases);\n if (dependency.releases.length > 0 || !looksLikeJson) {\n return dependency;\n }\n\n // The registry honored the `Accept` header but omitted or mislabeled the `content-type`, so a JSON body went through the HTML parser and found nothing. Parse it as JSON instead of silently reporting no releases.\n logger.debug(\n { packageName, hostUrl, contentType },\n 'Retrying Simple API response as JSON, as it is not labeled as JSON but looks like it',\n );\n const jsonReleases = PypiDatasource.getSimpleReleasesFromJson(\n dep,\n packageName,\n );\n if (!jsonReleases) {\n return null;\n }\n dependency.releases = PypiDatasource.toReleases(jsonReleases);\n return dependency;\n }\n\n /**\n * The files of a version are not ordered by upload time, so use the earliest one: that is when the version was first published, which is what `minimumReleaseAge` needs.\n */\n private static toReleases(releases: Releases): Release[] {\n const versions = Object.keys(releases);\n return versions.map((version) => {\n const versionReleases = coerceArray(releases[version]);\n const isDeprecated = versionReleases.some(({ yanked }) => yanked);\n const result: Release = { version };\n const releaseTimestamp =\n PypiDatasource.getEarliestTimestamp(versionReleases);\n if (releaseTimestamp) {\n result.releaseTimestamp = releaseTimestamp;\n }\n if (isDeprecated) {\n result.isDeprecated = isDeprecated;\n }\n const pythonConstraints = versionReleases.map(\n ({ requires_python }) => requires_python,\n );\n // A file without `requires_python` can be installed on any Python version, so the version as a whole is unconstrained\n const isUnconstrained = pythonConstraints.some(\n (constraint) => !isNonEmptyString(constraint),\n );\n // There may be multiple releases with different requires_python, so we return all in an array.\n // Report no constraints at all for an unconstrained version, instead of only those of its other files, which would drop the version under `constraintsFiltering=strict`.\n result.constraints = {\n python: isUnconstrained\n ? []\n : deduplicateArray(pythonConstraints.filter(isNonEmptyString)),\n };\n return result;\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["pep440.id","HttpError"],"sources":["../../../../lib/modules/datasource/pypi/index.ts"],"sourcesContent":["import { isNonEmptyString } from '@sindresorhus/is';\nimport changelogFilenameRegex from 'changelog-filename-regex';\nimport { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { coerceArray, deduplicateArray } from '../../../util/array.ts';\nimport { getEnv } from '../../../util/env.ts';\nimport { parse } from '../../../util/html.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport { isNotAcceptable } from '../../../util/http/status.ts';\nimport type {\n HttpResponse,\n OutgoingHttpHeaders,\n} from '../../../util/http/types.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { addSecretForSanitizing } from '../../../util/sanitize.ts';\nimport { Json } from '../../../util/schema-utils/index.ts';\nimport { toBase64 } from '../../../util/string.ts';\nimport type { Timestamp } from '../../../util/timestamp.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport { ensureTrailingSlash, parseUrl } from '../../../util/url.ts';\nimport * as pep440 from '../../versioning/pep440/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, Release, ReleaseResult } from '../types.ts';\nimport { isGoogleArtifactRegistry } from '../util.ts';\nimport {\n isGitHubRepo,\n normalizePythonDepName,\n pypiDatasourceId,\n} from './common.ts';\nimport { findPypiIndexCredentials } from './host-rules.ts';\nimport type { PypiRelease } from './schema.ts';\nimport { PypiResponse, PypiSimpleResponse } from './schema.ts';\nimport type { Releases } from './types.ts';\n\nfunction isSourceLabel(lower: string, projectUrl: string): boolean {\n return (\n lower.startsWith('repo') ||\n lower === 'code' ||\n lower === 'source' ||\n isGitHubRepo(projectUrl)\n );\n}\n\nfunction isChangelogLabel(lower: string): boolean {\n return (\n [\n 'changelog',\n 'change log',\n 'changes',\n 'release notes',\n 'news',\n \"what's new\",\n ].includes(lower) || !!changelogFilenameRegex.exec(lower)\n );\n}\n\nexport class PypiDatasource extends Datasource {\n static readonly id = pypiDatasourceId;\n\n constructor() {\n super(PypiDatasource.id);\n }\n\n override readonly caching = true;\n\n override supportsCustomRegistry(_packageName: string): boolean {\n return true;\n }\n\n static readonly defaultURL =\n getEnv().PIP_INDEX_URL ?? 'https://pypi.org/pypi/';\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return [PypiDatasource.defaultURL];\n }\n\n override readonly defaultVersioning = pep440.id;\n\n override readonly registryStrategy = 'merge';\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the earliest `upload_time` field of the files of a version. When using the Simple API, timestamps are available if the server supports the JSON-based Simple API (PEP 691).';\n override readonly sourceUrlSupport = 'release';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `homepage` field if it is a github repository, else we use the `project_urls` field.';\n\n async getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n let dependency: ReleaseResult | null = null;\n // TODO: null check (#22198)\n const hostUrl = ensureTrailingSlash(\n registryUrl!.replace('https://pypi.org/simple', 'https://pypi.org/pypi'),\n );\n const normalizedLookupName = normalizePythonDepName(packageName);\n\n // not all simple indexes use this identifier, but most do\n if (hostUrl.endsWith('/simple/') || hostUrl.endsWith('/+simple/')) {\n logger.trace(\n { packageName, hostUrl },\n 'Looking up pypi simple dependency',\n );\n dependency = await this.getSimpleDependency(\n normalizedLookupName,\n hostUrl,\n );\n } else {\n logger.trace({ packageName, hostUrl }, 'Looking up pypi api dependency');\n try {\n // we need to resolve early here so we can catch any 404s and fallback to a simple lookup\n dependency = await this.getDependency(normalizedLookupName, hostUrl);\n } catch (err) {\n // error contacting json-style api -- attempt to fallback to a simple-style api\n logger.trace(\n { packageName, hostUrl, err },\n 'Looking up pypi simple dependency via fallback',\n );\n dependency = await this.getSimpleDependency(\n normalizedLookupName,\n hostUrl,\n );\n }\n }\n return dependency;\n }\n\n private sanitizeLookupUrl(lookupUrl: string, parsedUrl: URL): string {\n if (!parsedUrl.username && !parsedUrl.password) {\n return lookupUrl;\n }\n\n parsedUrl.username = '';\n parsedUrl.password = '';\n return parsedUrl.toString();\n }\n\n private async getAuthHeaders(\n lookupUrl: string,\n ): Promise<{ headers: OutgoingHttpHeaders; lookupUrl: string }> {\n const parsedUrl = parseUrl(lookupUrl);\n // v8 ignore if -- TODO: refactor to cover this branch through public behavior again\n if (!parsedUrl) {\n logger.once.debug({ lookupUrl }, 'Failed to parse URL');\n return { headers: {}, lookupUrl };\n }\n // Only Google Artifact Registry needs a header of our own: for every other host `applyHostRule` already turns the matching host rule into request auth\n if (!isGoogleArtifactRegistry(parsedUrl.hostname)) {\n return { headers: {}, lookupUrl };\n }\n\n const { username, password } = await findPypiIndexCredentials(lookupUrl);\n if (!username && !password) {\n return { headers: {}, lookupUrl };\n }\n\n const auth = toBase64(`${username ?? ''}:${password ?? ''}`);\n addSecretForSanitizing(auth);\n return {\n headers: { authorization: `Basic ${auth}` },\n lookupUrl: this.sanitizeLookupUrl(lookupUrl, parsedUrl),\n };\n }\n\n private async getDependency(\n packageName: string,\n hostUrl: string,\n ): Promise<ReleaseResult | null> {\n const lookupUrl = new URL(\n `${normalizePythonDepName(packageName)}/json`,\n hostUrl,\n ).href;\n const dependency: ReleaseResult = { releases: [] };\n logger.trace({ lookupUrl }, 'Pypi api got lookup');\n const { headers, lookupUrl: sanitizedUrl } =\n await this.getAuthHeaders(lookupUrl);\n const rep = await this.http.getJson(\n sanitizedUrl,\n { headers },\n PypiResponse,\n );\n const dep = rep?.body;\n if (rep.authorization) {\n dependency.isPrivate = true;\n }\n logger.trace({ lookupUrl }, 'Got pypi api result');\n\n if (dep.info?.home_page) {\n dependency.homepage = dep.info.home_page;\n if (isGitHubRepo(dep.info.home_page)) {\n dependency.sourceUrl = dep.info.home_page.replace(\n 'http://',\n 'https://',\n );\n }\n }\n\n if (dep.info?.project_urls) {\n for (const [name, projectUrl] of Object.entries(dep.info.project_urls)) {\n const lower = name.toLowerCase();\n\n if (\n projectUrl &&\n !dependency.sourceUrl &&\n isSourceLabel(lower, projectUrl)\n ) {\n dependency.sourceUrl = projectUrl;\n }\n\n if (!dependency.changelogUrl && isChangelogLabel(lower)) {\n // from https://github.com/pypa/warehouse/blob/418c7511dc367fb410c71be139545d0134ccb0df/warehouse/templates/packaging/detail.html#L24\n dependency.changelogUrl = projectUrl;\n }\n }\n }\n\n if (dep.releases) {\n dependency.releases = PypiDatasource.toReleases(dep.releases);\n }\n return dependency;\n }\n\n private static getEarliestTimestamp(\n releases: PypiRelease[],\n ): Timestamp | null {\n let earliest: Timestamp | null = null;\n for (const { upload_time } of releases) {\n const timestamp = asTimestamp(upload_time);\n // `asTimestamp` normalizes to UTC ISO 8601, so comparing the strings compares the instants\n if (!timestamp) {\n continue;\n }\n if (!earliest || timestamp < earliest) {\n earliest = timestamp;\n }\n }\n return earliest;\n }\n\n private static extractVersionFromLinkText(\n text: string,\n packageName: string,\n ): string | null {\n // source packages\n const lcText = text.toLowerCase();\n const normalizedSrcText = normalizePythonDepName(text);\n const srcPrefix = `${packageName}-`;\n\n // source distribution format: `{name}-{version}.tar.gz` (https://packaging.python.org/en/latest/specifications/source-distribution-format/#source-distribution-file-name)\n // binary distribution: `{distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl` (https://packaging.python.org/en/latest/specifications/binary-distribution-format/#file-name-convention)\n // officially both `name` and `distribution` should be normalized and then the - replaced with _, but in reality this is not the case\n // We therefore normalize the name we have (replacing `_-.` with -) and then check if the text starts with the normalized name\n\n if (!normalizedSrcText.startsWith(srcPrefix)) {\n return null;\n }\n\n // strip off the prefix using the prefix length as we may have normalized the srcPrefix/packageName\n // We assume that neither the version nor the suffix contains multiple `-` like `0.1.2---rc1.tar.gz`\n // and use the difference in length to strip off the prefix in case the name contains double `--` characters\n const normalizedLengthDiff = lcText.length - normalizedSrcText.length;\n const res = lcText.slice(srcPrefix.length + normalizedLengthDiff);\n\n // source distribution\n const srcSuffixes = ['.tar.gz', '.tar.bz2', '.tar.xz', '.zip', '.tgz'];\n const srcSuffix = srcSuffixes.find((suffix) => lcText.endsWith(suffix));\n if (srcSuffix) {\n // strip off the suffix using character length\n return res.slice(0, -srcSuffix.length);\n }\n\n // binary distribution\n // for binary distributions the version is the first part after the removed distribution name\n const wheelSuffix = '.whl';\n if (lcText.endsWith(wheelSuffix) && lcText.split('-').length > 2) {\n return res.split('-')[0];\n }\n return null;\n }\n\n private static cleanSimpleHtml(html: string): string {\n return (\n html\n .replace(regEx(/<\\/?pre>/), '')\n // Certain simple repositories like artifactory don't escape > and <\n .replace(\n regEx(/data-requires-python=\"(?<before>[^\"]*?)>(?<after>[^\"]*?)\"/g),\n 'data-requires-python=\"$<before>&gt;$<after>\"',\n )\n .replace(\n regEx(/data-requires-python=\"(?<before>[^\"]*?)<(?<after>[^\"]*?)\"/g),\n 'data-requires-python=\"$<before>&lt;$<after>\"',\n )\n );\n }\n\n private static getSimpleReleasesFromHtml(\n html: string,\n packageName: string,\n ): Releases {\n const root = parse(PypiDatasource.cleanSimpleHtml(html));\n const links = root.querySelectorAll('a');\n const releases: Releases = {};\n for (const link of Array.from(links)) {\n const version = PypiDatasource.extractVersionFromLinkText(\n link.text?.trim(),\n packageName,\n );\n if (version) {\n const release: PypiRelease = {\n yanked: link.hasAttribute('data-yanked'),\n };\n const requiresPython = link.getAttribute('data-requires-python');\n if (requiresPython) {\n release.requires_python = requiresPython;\n }\n (releases[version] ??= []).push(release);\n }\n }\n return releases;\n }\n\n private static getSimpleReleasesFromJson(\n json: string,\n packageName: string,\n ): Releases | null {\n const releases: Releases = {};\n const parsed = Json.pipe(PypiSimpleResponse).safeParse(json);\n if (!parsed.success) {\n logger.once.warn(\n { packageName, err: parsed.error },\n 'Failed to parse JSON-based Simple API response',\n );\n // Distinguish a malformed response from a package with genuinely no files, so the caller doesn't mistake an error for an empty result.\n return null;\n }\n for (const file of parsed.data.files) {\n const version = PypiDatasource.extractVersionFromLinkText(\n file.filename,\n packageName,\n );\n if (version) {\n (releases[version] ??= []).push(file);\n }\n }\n return releases;\n }\n\n private async getSimpleDependency(\n packageName: string,\n hostUrl: string,\n ): Promise<ReleaseResult | null> {\n const lookupUrl = new URL(\n ensureTrailingSlash(normalizePythonDepName(packageName)),\n hostUrl,\n ).href;\n const dependency: ReleaseResult = { releases: [] };\n const { headers: authHeaders, lookupUrl: sanitizedUrl } =\n await this.getAuthHeaders(lookupUrl);\n const headers: OutgoingHttpHeaders = {\n ...authHeaders,\n // Request the JSON serialization (PEP 691), falling back to the legacy HTML serialization (PEP 503) via content negotiation.\n // https://github.com/pypa/pip/blob/6b0011b49a068c62f65389bf4cea7af5d28cb002/src/pip/_internal/index/collector.py#L128-L134\n accept:\n 'application/vnd.pypi.simple.v1+json, application/vnd.pypi.simple.v1+html; q=0.1, text/html; q=0.01',\n };\n let response: HttpResponse;\n try {\n response = await this.http.getText(sanitizedUrl, { headers });\n } catch (err) {\n // An `abortOnError` host rule makes `Http` wrap the original error, so unwrap it before looking at the status code.\n const httpErr = err instanceof ExternalHostError ? err.err : err;\n const statusCode =\n httpErr instanceof HttpError ? httpErr.response?.statusCode : undefined;\n // A registry which cannot serve any of the negotiated types may pick a content type of its own, answer `406`, or answer `300`; the spec mandates none of them, but `406` is the common choice.\n // https://packaging.python.org/en/latest/specifications/simple-repository-api/#version-format-selection\n // Retry once without the negotiated `accept` header to preserve the pre-PEP-691 behaviour of a plain request.\n if (!isNotAcceptable(statusCode)) {\n throw err;\n }\n\n logger.trace(\n { packageName, hostUrl, statusCode },\n 'Registry rejected negotiated Accept header, retrying without it',\n );\n response = await this.http.getText(sanitizedUrl, {\n headers: authHeaders,\n });\n }\n const dep = response?.body;\n if (!dep) {\n logger.trace({ dependency: packageName }, 'pip package not found');\n return null;\n }\n if (response.authorization) {\n dependency.isPrivate = true;\n }\n\n // Dispatch on the response content-type: JSON serialization (PEP 691) or the legacy HTML serialization (PEP 503). Matched loosely (any `json` media type) rather than the exact vendor type, as some registries/proxies relabel or strip the vendor-specific media type while still returning JSON.\n const contentType = response.headers['content-type'];\n const isJson = !!contentType && contentType.includes('json');\n const looksLikeJson = dep.trimStart().startsWith('{');\n\n if (isJson) {\n if (looksLikeJson) {\n const releases = PypiDatasource.getSimpleReleasesFromJson(\n dep,\n packageName,\n );\n // A parse failure is distinct from a package with genuinely no files, so don't report an empty release list as if it were a real (if empty) result, which would be cached as such.\n if (!releases) {\n return null;\n }\n dependency.releases = PypiDatasource.toReleases(releases);\n return dependency;\n }\n\n // The registry mislabeled a non-JSON body as JSON, so fall through to the HTML parser instead of reporting the package as not found.\n logger.debug(\n { packageName, hostUrl, contentType },\n 'Parsing Simple API response as HTML, as it is labeled as JSON but does not look like it',\n );\n }\n\n const htmlReleases = PypiDatasource.getSimpleReleasesFromHtml(\n dep,\n packageName,\n );\n dependency.releases = PypiDatasource.toReleases(htmlReleases);\n if (dependency.releases.length > 0 || !looksLikeJson) {\n return dependency;\n }\n\n // The registry honored the `Accept` header but omitted or mislabeled the `content-type`, so a JSON body went through the HTML parser and found nothing. Parse it as JSON instead of silently reporting no releases.\n logger.debug(\n { packageName, hostUrl, contentType },\n 'Retrying Simple API response as JSON, as it is not labeled as JSON but looks like it',\n );\n const jsonReleases = PypiDatasource.getSimpleReleasesFromJson(\n dep,\n packageName,\n );\n if (!jsonReleases) {\n return null;\n }\n dependency.releases = PypiDatasource.toReleases(jsonReleases);\n return dependency;\n }\n\n /**\n * The files of a version are not ordered by upload time, so use the earliest one: that is when the version was first published, which is what `minimumReleaseAge` needs.\n */\n private static toReleases(releases: Releases): Release[] {\n const versions = Object.keys(releases);\n return versions.map((version) => {\n const versionReleases = coerceArray(releases[version]);\n const isDeprecated = versionReleases.some(({ yanked }) => yanked);\n const result: Release = { version };\n const releaseTimestamp =\n PypiDatasource.getEarliestTimestamp(versionReleases);\n if (releaseTimestamp) {\n result.releaseTimestamp = releaseTimestamp;\n }\n if (isDeprecated) {\n result.isDeprecated = isDeprecated;\n }\n const pythonConstraints = versionReleases.map(\n ({ requires_python }) => requires_python,\n );\n // A file without `requires_python` can be installed on any Python version, so the version as a whole is unconstrained\n const isUnconstrained = pythonConstraints.some(\n (constraint) => !isNonEmptyString(constraint),\n );\n // There may be multiple releases with different requires_python, so we return all in an array.\n // Report no constraints at all for an unconstrained version, instead of only those of its other files, which would drop the version under `constraintsFiltering=strict`.\n result.constraints = {\n python: isUnconstrained\n ? []\n : deduplicateArray(pythonConstraints.filter(isNonEmptyString)),\n };\n return result;\n });\n }\n}\n"],"mappings":"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"}
@@ -1,8 +1,7 @@
1
1
  import { coerceArray } from "../../../util/array.js";
2
2
  import { logger } from "../../../logger/index.js";
3
+ import { isRateLimited } from "../../../util/http/status.js";
3
4
  import { id } from "../../versioning/python/index.js";
4
- import { RequestError } from "../../../util/http/got.js";
5
- import "../../../util/http/index.js";
6
5
  import { Datasource } from "../datasource.js";
7
6
  import { registryUrl } from "../endoflife-date/common.js";
8
7
  import { EndoflifeDateDatasource } from "../endoflife-date/index.js";
@@ -55,7 +54,7 @@ var PythonVersionDatasource = class extends Datasource {
55
54
  const response = await this.http.getJson(registryUrl, PythonRelease);
56
55
  result.releases.push(...response.body.filter((release) => release.isStable).filter((release) => pythonPrebuildVersions.has(release.version)));
57
56
  } catch (err) {
58
- if (err instanceof RequestError && err.response?.statusCode === 429) {
57
+ if (isRateLimited(err)) {
59
58
  logger.debug({ err }, "Rate limited by python.org, using prebuild releases");
60
59
  result.releases.push(...coerceArray(pythonPrebuildReleases?.releases));
61
60
  } else this.handleGenericErrors(err);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["versioning","eolRegistryUrl","HttpError"],"sources":["../../../../lib/modules/datasource/python-version/index.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { coerceArray } from '../../../util/array.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport { id as versioning } from '../../versioning/python/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport { registryUrl as eolRegistryUrl } from '../endoflife-date/common.ts';\nimport { EndoflifeDateDatasource } from '../endoflife-date/index.ts';\nimport { GithubReleasesDatasource } from '../github-releases/index.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { datasource, defaultRegistryUrl, githubBaseUrl } from './common.ts';\nimport { PythonRelease } from './schema.ts';\n\nexport class PythonVersionDatasource extends Datasource {\n static readonly id = datasource;\n pythonPrebuildDatasource: GithubReleasesDatasource;\n pythonEolDatasource: EndoflifeDateDatasource;\n\n constructor() {\n super(datasource);\n this.pythonPrebuildDatasource = new GithubReleasesDatasource();\n this.pythonEolDatasource = new EndoflifeDateDatasource();\n }\n\n override supportsCustomRegistry(_packageName: string): boolean {\n return false;\n }\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return [defaultRegistryUrl];\n }\n\n override readonly defaultVersioning = versioning;\n\n async getPrebuildReleases(): Promise<ReleaseResult | null> {\n return await this.pythonPrebuildDatasource.getReleases({\n registryUrl: githubBaseUrl,\n packageName: 'containerbase/python-prebuild',\n });\n }\n\n async getEolReleases(): Promise<ReleaseResult | null> {\n return await this.pythonEolDatasource.getReleases({\n registryUrl: eolRegistryUrl,\n packageName: 'python',\n });\n }\n\n private async fetchReleases({\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n const pythonPrebuildReleases = await this.getPrebuildReleases();\n const pythonPrebuildVersions = new Set<string>(\n pythonPrebuildReleases?.releases.map((release) => release.version),\n );\n const pythonEolReleases = await this.getEolReleases();\n const pythonEolVersions = new Map(\n pythonEolReleases?.releases\n .filter((release) => release.isDeprecated !== undefined)\n .map((release) => [\n release.version.split('.').slice(0, 2).join('.'),\n release.isDeprecated,\n ]),\n );\n const result: ReleaseResult = {\n homepage: 'https://python.org',\n sourceUrl: 'https://github.com/python/cpython',\n registryUrl,\n releases: [],\n };\n try {\n const response = await this.http.getJson(registryUrl, PythonRelease);\n result.releases.push(\n ...response.body\n .filter((release) => release.isStable)\n .filter((release) => pythonPrebuildVersions.has(release.version)),\n );\n } catch (err) {\n if (err instanceof HttpError && err.response?.statusCode === 429) {\n logger.debug(\n { err },\n 'Rate limited by python.org, using prebuild releases',\n );\n result.releases.push(...coerceArray(pythonPrebuildReleases?.releases));\n } else {\n this.handleGenericErrors(err);\n }\n }\n for (const release of result.releases) {\n release.isDeprecated = pythonEolVersions.get(\n release.version.split('.').slice(0, 2).join('.'),\n );\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: config.registryUrl!,\n fallback: true,\n },\n () => this.fetchReleases(config),\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;AAYA,IAAa,0BAAb,cAA6C,WAAW;CACtD,OAAgB,KAAK;CACrB;CACA;CAEA,cAAc;EACZ,MAAM,UAAU;EAChB,KAAK,2BAA2B,IAAI,yBAAyB;EAC7D,KAAK,sBAAsB,IAAI,wBAAwB;CACzD;CAEA,uBAAgC,cAA+B;EAC7D,OAAO;CACT;CAEA,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,kBAAkB;CAC5B;CAEA,oBAAsCA;CAEtC,MAAM,sBAAqD;EACzD,OAAO,MAAM,KAAK,yBAAyB,YAAY;GACrD,aAAa;GACb,aAAa;EACf,CAAC;CACH;CAEA,MAAM,iBAAgD;EACpD,OAAO,MAAM,KAAK,oBAAoB,YAAY;GACnCC;GACb,aAAa;EACf,CAAC;CACH;CAEA,MAAc,cAAc,EAC1B,eACmD;;EAEnD,IAAI,CAAC,aACH,OAAO;EAET,MAAM,yBAAyB,MAAM,KAAK,oBAAoB;EAC9D,MAAM,yBAAyB,IAAI,IACjC,wBAAwB,SAAS,KAAK,YAAY,QAAQ,OAAO,CACnE;EACA,MAAM,oBAAoB,MAAM,KAAK,eAAe;EACpD,MAAM,oBAAoB,IAAI,IAC5B,mBAAmB,SAChB,QAAQ,YAAY,QAAQ,iBAAiB,KAAA,CAAS,CAAC,CACvD,KAAK,YAAY,CAChB,QAAQ,QAAQ,MAAM,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,GAC/C,QAAQ,YACV,CAAC,CACL;EACA,MAAM,SAAwB;GAC5B,UAAU;GACV,WAAW;GACX;GACA,UAAU,CAAC;EACb;EACA,IAAI;GACF,MAAM,WAAW,MAAM,KAAK,KAAK,QAAQ,aAAa,aAAa;GACnE,OAAO,SAAS,KACd,GAAG,SAAS,KACT,QAAQ,YAAY,QAAQ,QAAQ,CAAC,CACrC,QAAQ,YAAY,uBAAuB,IAAI,QAAQ,OAAO,CAAC,CACpE;EACF,SAAS,KAAK;GACZ,IAAI,eAAeC,gBAAa,IAAI,UAAU,eAAe,KAAK;IAChE,OAAO,MACL,EAAE,IAAI,GACN,qDACF;IACA,OAAO,SAAS,KAAK,GAAG,YAAY,wBAAwB,QAAQ,CAAC;GACvE,OACE,KAAK,oBAAoB,GAAG;EAEhC;EACA,KAAK,MAAM,WAAW,OAAO,UAC3B,QAAQ,eAAe,kBAAkB,IACvC,QAAQ,QAAQ,MAAM,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CACjD;EAGF,OAAO,OAAO,SAAS,SAAS,SAAS;CAC3C;CAEA,YAAY,QAA0D;EACpE,OAAO,KAAK,OACV;GACE,KAAK,OAAO;GACZ,UAAU;EACZ,SACM,KAAK,cAAc,MAAM,CACjC;CACF;AACF"}
1
+ {"version":3,"file":"index.js","names":["versioning","eolRegistryUrl"],"sources":["../../../../lib/modules/datasource/python-version/index.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { coerceArray } from '../../../util/array.ts';\nimport { isRateLimited } from '../../../util/http/status.ts';\nimport { id as versioning } from '../../versioning/python/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport { registryUrl as eolRegistryUrl } from '../endoflife-date/common.ts';\nimport { EndoflifeDateDatasource } from '../endoflife-date/index.ts';\nimport { GithubReleasesDatasource } from '../github-releases/index.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { datasource, defaultRegistryUrl, githubBaseUrl } from './common.ts';\nimport { PythonRelease } from './schema.ts';\n\nexport class PythonVersionDatasource extends Datasource {\n static readonly id = datasource;\n pythonPrebuildDatasource: GithubReleasesDatasource;\n pythonEolDatasource: EndoflifeDateDatasource;\n\n constructor() {\n super(datasource);\n this.pythonPrebuildDatasource = new GithubReleasesDatasource();\n this.pythonEolDatasource = new EndoflifeDateDatasource();\n }\n\n override supportsCustomRegistry(_packageName: string): boolean {\n return false;\n }\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return [defaultRegistryUrl];\n }\n\n override readonly defaultVersioning = versioning;\n\n async getPrebuildReleases(): Promise<ReleaseResult | null> {\n return await this.pythonPrebuildDatasource.getReleases({\n registryUrl: githubBaseUrl,\n packageName: 'containerbase/python-prebuild',\n });\n }\n\n async getEolReleases(): Promise<ReleaseResult | null> {\n return await this.pythonEolDatasource.getReleases({\n registryUrl: eolRegistryUrl,\n packageName: 'python',\n });\n }\n\n private async fetchReleases({\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n const pythonPrebuildReleases = await this.getPrebuildReleases();\n const pythonPrebuildVersions = new Set<string>(\n pythonPrebuildReleases?.releases.map((release) => release.version),\n );\n const pythonEolReleases = await this.getEolReleases();\n const pythonEolVersions = new Map(\n pythonEolReleases?.releases\n .filter((release) => release.isDeprecated !== undefined)\n .map((release) => [\n release.version.split('.').slice(0, 2).join('.'),\n release.isDeprecated,\n ]),\n );\n const result: ReleaseResult = {\n homepage: 'https://python.org',\n sourceUrl: 'https://github.com/python/cpython',\n registryUrl,\n releases: [],\n };\n try {\n const response = await this.http.getJson(registryUrl, PythonRelease);\n result.releases.push(\n ...response.body\n .filter((release) => release.isStable)\n .filter((release) => pythonPrebuildVersions.has(release.version)),\n );\n } catch (err) {\n if (isRateLimited(err)) {\n logger.debug(\n { err },\n 'Rate limited by python.org, using prebuild releases',\n );\n result.releases.push(...coerceArray(pythonPrebuildReleases?.releases));\n } else {\n this.handleGenericErrors(err);\n }\n }\n for (const release of result.releases) {\n release.isDeprecated = pythonEolVersions.get(\n release.version.split('.').slice(0, 2).join('.'),\n );\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: config.registryUrl!,\n fallback: true,\n },\n () => this.fetchReleases(config),\n );\n }\n}\n"],"mappings":";;;;;;;;;;;AAYA,IAAa,0BAAb,cAA6C,WAAW;CACtD,OAAgB,KAAK;CACrB;CACA;CAEA,cAAc;EACZ,MAAM,UAAU;EAChB,KAAK,2BAA2B,IAAI,yBAAyB;EAC7D,KAAK,sBAAsB,IAAI,wBAAwB;CACzD;CAEA,uBAAgC,cAA+B;EAC7D,OAAO;CACT;CAEA,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,kBAAkB;CAC5B;CAEA,oBAAsCA;CAEtC,MAAM,sBAAqD;EACzD,OAAO,MAAM,KAAK,yBAAyB,YAAY;GACrD,aAAa;GACb,aAAa;EACf,CAAC;CACH;CAEA,MAAM,iBAAgD;EACpD,OAAO,MAAM,KAAK,oBAAoB,YAAY;GACnCC;GACb,aAAa;EACf,CAAC;CACH;CAEA,MAAc,cAAc,EAC1B,eACmD;;EAEnD,IAAI,CAAC,aACH,OAAO;EAET,MAAM,yBAAyB,MAAM,KAAK,oBAAoB;EAC9D,MAAM,yBAAyB,IAAI,IACjC,wBAAwB,SAAS,KAAK,YAAY,QAAQ,OAAO,CACnE;EACA,MAAM,oBAAoB,MAAM,KAAK,eAAe;EACpD,MAAM,oBAAoB,IAAI,IAC5B,mBAAmB,SAChB,QAAQ,YAAY,QAAQ,iBAAiB,KAAA,CAAS,CAAC,CACvD,KAAK,YAAY,CAChB,QAAQ,QAAQ,MAAM,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,GAC/C,QAAQ,YACV,CAAC,CACL;EACA,MAAM,SAAwB;GAC5B,UAAU;GACV,WAAW;GACX;GACA,UAAU,CAAC;EACb;EACA,IAAI;GACF,MAAM,WAAW,MAAM,KAAK,KAAK,QAAQ,aAAa,aAAa;GACnE,OAAO,SAAS,KACd,GAAG,SAAS,KACT,QAAQ,YAAY,QAAQ,QAAQ,CAAC,CACrC,QAAQ,YAAY,uBAAuB,IAAI,QAAQ,OAAO,CAAC,CACpE;EACF,SAAS,KAAK;GACZ,IAAI,cAAc,GAAG,GAAG;IACtB,OAAO,MACL,EAAE,IAAI,GACN,qDACF;IACA,OAAO,SAAS,KAAK,GAAG,YAAY,wBAAwB,QAAQ,CAAC;GACvE,OACE,KAAK,oBAAoB,GAAG;EAEhC;EACA,KAAK,MAAM,WAAW,OAAO,UAC3B,QAAQ,eAAe,kBAAkB,IACvC,QAAQ,QAAQ,MAAM,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CACjD;EAGF,OAAO,OAAO,SAAS,SAAS,SAAS;CAC3C;CAEA,YAAY,QAA0D;EACpE,OAAO,KAAK,OACV;GACE,KAAK,OAAO;GACZ,UAAU;EACZ,SACM,KAAK,cAAc,MAAM,CACjC;CACF;AACF"}
@@ -2,6 +2,7 @@ import "../../../constants/error-messages.js";
2
2
  import { logger } from "../../../logger/index.js";
3
3
  import { buildCacheKey } from "../../../util/cache/package/key.js";
4
4
  import { getQueryString, joinUrlParts } from "../../../util/url.js";
5
+ import { isForbidden, isMultipleChoices, isNotFound } from "../../../util/http/status.js";
5
6
  import { ExternalHostError } from "../../../types/errors/external-host-error.js";
6
7
  import { withCache } from "../../../util/cache/package/with-cache.js";
7
8
  import { refusedHostMessage } from "../../../util/http/util.js";
@@ -33,7 +34,7 @@ var RepologyDatasource = class RepologyDatasource extends Datasource {
33
34
  try {
34
35
  return (await this.http.getJson(url, RepologyPackages)).body;
35
36
  } catch (err) {
36
- if (err.statusCode === 404) return [];
37
+ if (isNotFound(err)) return [];
37
38
  throw err;
38
39
  }
39
40
  }
@@ -62,7 +63,7 @@ var RepologyDatasource = class RepologyDatasource extends Datasource {
62
63
  }
63
64
  }
64
65
  } catch (err) {
65
- if (err.statusCode === 403) {
66
+ if (isForbidden(err)) {
66
67
  logger.debug({
67
68
  repoName,
68
69
  pkgName
@@ -70,7 +71,7 @@ var RepologyDatasource = class RepologyDatasource extends Datasource {
70
71
  response = await this.queryPackagesViaAPI(registryUrl, pkgName);
71
72
  const pkg = findPackageInResponse(response, repoName, pkgName, packageTypes);
72
73
  if (isNonEmptyArray(pkg)) return pkg;
73
- } else if (err.statusCode === 300) {
74
+ } else if (isMultipleChoices(err)) {
74
75
  logger.warn({
75
76
  repoName,
76
77
  pkgName