renovate 44.32.6 → 44.33.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.
- package/dist/modules/datasource/deb/checksum.js +2 -2
- package/dist/modules/datasource/deb/checksum.js.map +1 -1
- package/dist/modules/datasource/java-version/adoptium.js +48 -0
- package/dist/modules/datasource/java-version/adoptium.js.map +1 -0
- package/dist/modules/datasource/java-version/index.js +5 -34
- package/dist/modules/datasource/java-version/index.js.map +1 -1
- package/dist/modules/datasource/pypi/index.js +94 -31
- package/dist/modules/datasource/pypi/index.js.map +1 -1
- package/dist/modules/datasource/pypi/schema.js +15 -2
- package/dist/modules/datasource/pypi/schema.js.map +1 -1
- package/dist/modules/manager/ant/properties.js +2 -2
- package/dist/modules/manager/ant/properties.js.map +1 -1
- package/dist/modules/manager/bazel/rules/http.js +2 -2
- package/dist/modules/manager/bazel/rules/http.js.map +1 -1
- package/dist/modules/manager/fingerprint.generated.js +1 -1
- package/dist/modules/manager/fingerprint.generated.js.map +1 -1
- package/dist/modules/manager/github-actions/community.js +2 -2
- package/dist/modules/manager/github-actions/community.js.map +1 -1
- package/dist/modules/manager/gradle/extract/catalog.js +3 -3
- package/dist/modules/manager/gradle/extract/catalog.js.map +1 -1
- package/dist/modules/manager/homebrew/utils.js +3 -3
- package/dist/modules/manager/homebrew/utils.js.map +1 -1
- package/dist/modules/manager/mise/backends.js +2 -2
- package/dist/modules/manager/mise/backends.js.map +1 -1
- package/dist/modules/manager/npm/update/dependency/index.js +2 -2
- package/dist/modules/manager/npm/update/dependency/index.js.map +1 -1
- package/dist/modules/manager/pip_requirements/artifacts.js +2 -2
- package/dist/modules/manager/pip_requirements/artifacts.js.map +1 -1
- package/dist/modules/manager/swift/artifacts.js +2 -2
- package/dist/modules/manager/swift/artifacts.js.map +1 -1
- package/dist/modules/manager/terraform/lockfile/index.js +2 -2
- package/dist/modules/manager/terraform/lockfile/index.js.map +1 -1
- package/dist/util/regex.js +1 -9
- package/dist/util/regex.js.map +1 -1
- package/dist/workers/repository/finalize/prune.js +3 -3
- package/dist/workers/repository/finalize/prune.js.map +1 -1
- package/dist/workers/repository/init/vulnerability.js +2 -2
- package/dist/workers/repository/init/vulnerability.js.map +1 -1
- package/dist/workers/repository/update/branch/auto-replace.js +5 -5
- package/dist/workers/repository/update/branch/auto-replace.js.map +1 -1
- package/package.json +1 -1
- package/renovate-schema.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { newlineRegex, regEx } from "../../../util/regex.js";
|
|
2
2
|
import { hashStream } from "../../../util/hash.js";
|
|
3
3
|
import { createCacheReadStream } from "../../../util/fs/index.js";
|
|
4
4
|
//#region lib/modules/datasource/deb/checksum.ts
|
|
@@ -11,7 +11,7 @@ import { createCacheReadStream } from "../../../util/fs/index.js";
|
|
|
11
11
|
*/
|
|
12
12
|
function parseChecksumsFromInRelease(inReleaseContent, packagePath) {
|
|
13
13
|
const lines = inReleaseContent.split(newlineRegex);
|
|
14
|
-
const regex = regEx(`([a-f0-9]{64})\\s+\\d+\\s+${
|
|
14
|
+
const regex = regEx(`([a-f0-9]{64})\\s+\\d+\\s+${RegExp.escape(packagePath)}$`);
|
|
15
15
|
for (const line of lines) {
|
|
16
16
|
const match = regex.exec(line);
|
|
17
17
|
if (match) return match[1];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checksum.js","names":[],"sources":["../../../../lib/modules/datasource/deb/checksum.ts"],"sourcesContent":["import { createCacheReadStream } from '../../../util/fs/index.ts';\nimport { hashStream } from '../../../util/hash.ts';\nimport {
|
|
1
|
+
{"version":3,"file":"checksum.js","names":[],"sources":["../../../../lib/modules/datasource/deb/checksum.ts"],"sourcesContent":["import { createCacheReadStream } from '../../../util/fs/index.ts';\nimport { hashStream } from '../../../util/hash.ts';\nimport { newlineRegex, regEx } from '../../../util/regex.ts';\n\n/**\n * Parses the SHA256 checksum for a specified package path from the InRelease content.\n *\n * @param inReleaseContent - content of the InRelease file\n * @param packagePath - path of the package file (e.g., 'contrib/binary-amd64/Packages.gz')\n * @returns The SHA256 checksum if found, otherwise undefined\n */\nexport function parseChecksumsFromInRelease(\n inReleaseContent: string,\n packagePath: string,\n): string | null {\n const lines = inReleaseContent.split(newlineRegex);\n const regex = regEx(\n `([a-f0-9]{64})\\\\s+\\\\d+\\\\s+${RegExp.escape(packagePath)}$`,\n );\n\n for (const line of lines) {\n const match = regex.exec(line);\n if (match) {\n return match[1];\n }\n }\n\n return null;\n}\n\n/**\n * Computes the SHA256 checksum of a specified file.\n *\n * @param filePath - path of the file\n * @returns resolves to the SHA256 checksum\n */\nexport function computeFileChecksum(filePath: string): Promise<string> {\n const stream = createCacheReadStream(filePath);\n return hashStream(stream, 'sha256');\n}\n"],"mappings":";;;;;;;;;;;AAWA,SAAgB,4BACd,kBACA,aACe;CACf,MAAM,QAAQ,iBAAiB,MAAM,YAAY;CACjD,MAAM,QAAQ,MACZ,6BAA6B,OAAO,OAAO,WAAW,EAAE,EAC1D;CAEA,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,QAAQ,MAAM,KAAK,IAAI;EAC7B,IAAI,OACF,OAAO,MAAM;CAEjB;CAEA,OAAO;AACT;;;;;;;AAQA,SAAgB,oBAAoB,UAAmC;CACrE,MAAM,SAAS,sBAAsB,QAAQ;CAC7C,OAAO,WAAW,QAAQ,QAAQ;AACpC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { logger } from "../../../logger/index.js";
|
|
2
|
+
import { ExternalHostError } from "../../../types/errors/external-host-error.js";
|
|
3
|
+
import { RequestError } from "../../../util/http/got.js";
|
|
4
|
+
import "../../../util/http/index.js";
|
|
5
|
+
import "./common.js";
|
|
6
|
+
import { AdoptiumJavaResponse } from "./schema.js";
|
|
7
|
+
//#region lib/modules/datasource/java-version/adoptium.ts
|
|
8
|
+
const adoptiumRegistryUrl = "https://api.adoptium.net/";
|
|
9
|
+
async function getPageReleases(http, url, page) {
|
|
10
|
+
const pgUrl = `${url}&page=${page}`;
|
|
11
|
+
try {
|
|
12
|
+
return (await http.getJson(pgUrl, AdoptiumJavaResponse))?.body?.versions?.map(({ semver }) => ({ version: semver })) ?? null;
|
|
13
|
+
} catch (err) {
|
|
14
|
+
if (page !== 0 && err instanceof RequestError && err.response?.statusCode === 404) return null;
|
|
15
|
+
throw err;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async function getAdoptiumReleases(http, pkgConfig) {
|
|
19
|
+
logger.trace({ pkgConfig }, "fetching Adoptium releases");
|
|
20
|
+
let url = `${adoptiumRegistryUrl}v3/info/release_versions?page_size=50&image_type=${pkgConfig.imageType}&project=jdk&release_type=ga&sort_method=DATE&sort_order=DESC`;
|
|
21
|
+
if (pkgConfig.architecture) url += `&architecture=${pkgConfig.architecture}`;
|
|
22
|
+
if (pkgConfig.os) url += `&os=${pkgConfig.os}`;
|
|
23
|
+
const result = {
|
|
24
|
+
homepage: "https://adoptium.net",
|
|
25
|
+
releases: []
|
|
26
|
+
};
|
|
27
|
+
try {
|
|
28
|
+
let page = 0;
|
|
29
|
+
let releases = await getPageReleases(http, url, page);
|
|
30
|
+
while (releases) {
|
|
31
|
+
result.releases.push(...releases);
|
|
32
|
+
if (releases.length !== 50 || page >= 50) break;
|
|
33
|
+
page += 1;
|
|
34
|
+
releases = await getPageReleases(http, url, page);
|
|
35
|
+
}
|
|
36
|
+
} catch (err) {
|
|
37
|
+
if (err instanceof RequestError) {
|
|
38
|
+
if (err.response?.statusCode !== 404) throw new ExternalHostError(err);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
43
|
+
return result.releases.length ? result : null;
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { adoptiumRegistryUrl, getAdoptiumReleases };
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=adoptium.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adoptium.js","names":["HttpError"],"sources":["../../../../lib/modules/datasource/java-version/adoptium.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { type Http, HttpError } from '../../../util/http/index.ts';\nimport type { Release, ReleaseResult } from '../types.ts';\nimport { pageSize } from './common.ts';\nimport { AdoptiumJavaResponse } from './schema.ts';\nimport type { PackageConfig } from './types.ts';\n\nexport const adoptiumRegistryUrl = 'https://api.adoptium.net/';\n\nasync function getPageReleases(\n http: Http,\n url: string,\n page: number,\n): Promise<Release[] | null> {\n const pgUrl = `${url}&page=${page}`;\n try {\n const pgRes = await http.getJson(pgUrl, AdoptiumJavaResponse);\n return (\n pgRes?.body?.versions?.map(({ semver }) => ({\n version: semver,\n })) ?? null\n );\n } catch (err) {\n if (\n page !== 0 &&\n err instanceof HttpError &&\n err.response?.statusCode === 404\n ) {\n // No more pages\n return null;\n }\n\n throw err;\n }\n}\n\nexport async function getAdoptiumReleases(\n http: Http,\n pkgConfig: PackageConfig,\n): Promise<ReleaseResult | null> {\n logger.trace({ pkgConfig }, 'fetching Adoptium releases');\n\n let url = `${adoptiumRegistryUrl}v3/info/release_versions?page_size=${pageSize}&image_type=${pkgConfig.imageType}&project=jdk&release_type=ga&sort_method=DATE&sort_order=DESC`;\n\n if (pkgConfig.architecture) {\n url += `&architecture=${pkgConfig.architecture}`;\n }\n if (pkgConfig.os) {\n url += `&os=${pkgConfig.os}`;\n }\n\n const result: ReleaseResult = {\n homepage: 'https://adoptium.net',\n releases: [],\n };\n\n try {\n let page = 0;\n let releases = await getPageReleases(http, url, page);\n while (releases) {\n result.releases.push(...releases);\n if (releases.length !== pageSize || page >= 50) {\n break;\n }\n page += 1;\n releases = await getPageReleases(http, url, page);\n }\n } catch (err) {\n if (err instanceof HttpError) {\n if (err.response?.statusCode !== 404) {\n throw new ExternalHostError(err);\n }\n // 404 is handled by getPageReleases, so if we get here it's an unexpected 404\n return null;\n }\n throw err;\n }\n\n return result.releases.length ? result : null;\n}\n"],"mappings":";;;;;;;AAQA,MAAa,sBAAsB;AAEnC,eAAe,gBACb,MACA,KACA,MAC2B;CAC3B,MAAM,QAAQ,GAAG,IAAI,QAAQ;CAC7B,IAAI;EAEF,QACE,MAFkB,KAAK,QAAQ,OAAO,oBAAoB,EAAA,EAEnD,MAAM,UAAU,KAAK,EAAE,cAAc,EAC1C,SAAS,OACX,EAAE,KAAK;CAEX,SAAS,KAAK;EACZ,IACE,SAAS,KACT,eAAeA,gBACf,IAAI,UAAU,eAAe,KAG7B,OAAO;EAGT,MAAM;CACR;AACF;AAEA,eAAsB,oBACpB,MACA,WAC+B;CAC/B,OAAO,MAAM,EAAE,UAAU,GAAG,4BAA4B;CAExD,IAAI,MAAM,GAAG,oBAAoB,mDAA4D,UAAU,UAAU;CAEjH,IAAI,UAAU,cACZ,OAAO,iBAAiB,UAAU;CAEpC,IAAI,UAAU,IACZ,OAAO,OAAO,UAAU;CAG1B,MAAM,SAAwB;EAC5B,UAAU;EACV,UAAU,CAAC;CACb;CAEA,IAAI;EACF,IAAI,OAAO;EACX,IAAI,WAAW,MAAM,gBAAgB,MAAM,KAAK,IAAI;EACpD,OAAO,UAAU;GACf,OAAO,SAAS,KAAK,GAAG,QAAQ;GAChC,IAAI,SAAS,WAAA,MAAuB,QAAQ,IAC1C;GAEF,QAAQ;GACR,WAAW,MAAM,gBAAgB,MAAM,KAAK,IAAI;EAClD;CACF,SAAS,KAAK;EACZ,IAAI,eAAeA,cAAW;GAC5B,IAAI,IAAI,UAAU,eAAe,KAC/B,MAAM,IAAI,kBAAkB,GAAG;GAGjC,OAAO;EACT;EACA,MAAM;CACR;CAEA,OAAO,OAAO,SAAS,SAAS,SAAS;AAC3C"}
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { logger } from "../../../logger/index.js";
|
|
2
|
-
import { ExternalHostError } from "../../../types/errors/external-host-error.js";
|
|
3
2
|
import { withCache } from "../../../util/cache/package/with-cache.js";
|
|
4
|
-
import { RequestError } from "../../../util/http/got.js";
|
|
5
|
-
import "../../../util/http/index.js";
|
|
6
3
|
import { Datasource } from "../datasource.js";
|
|
7
|
-
import { datasource,
|
|
8
|
-
import {
|
|
4
|
+
import { datasource, parsePackage } from "./common.js";
|
|
5
|
+
import { adoptiumRegistryUrl, getAdoptiumReleases } from "./adoptium.js";
|
|
9
6
|
//#region lib/modules/datasource/java-version/index.ts
|
|
10
7
|
var JavaVersionDatasource = class extends Datasource {
|
|
11
8
|
static id = datasource;
|
|
@@ -13,45 +10,19 @@ var JavaVersionDatasource = class extends Datasource {
|
|
|
13
10
|
super(datasource);
|
|
14
11
|
}
|
|
15
12
|
customRegistrySupport = false;
|
|
16
|
-
defaultRegistryUrls = [
|
|
13
|
+
defaultRegistryUrls = [adoptiumRegistryUrl];
|
|
17
14
|
caching = true;
|
|
18
|
-
async
|
|
19
|
-
const pgUrl = `${url}&page=${page}`;
|
|
20
|
-
try {
|
|
21
|
-
return (await this.http.getJson(pgUrl, AdoptiumJavaResponse))?.body?.versions?.map(({ semver }) => ({ version: semver })) ?? null;
|
|
22
|
-
} catch (err) {
|
|
23
|
-
if (page !== 0 && err instanceof RequestError && err.response?.statusCode === 404) return null;
|
|
24
|
-
throw err;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
async _getReleases({ registryUrl, packageName }) {
|
|
15
|
+
async _getReleases({ packageName }) {
|
|
28
16
|
const pkgConfig = parsePackage(packageName);
|
|
29
17
|
logger.trace({
|
|
30
|
-
registryUrl,
|
|
31
18
|
packageName,
|
|
32
19
|
pkgConfig
|
|
33
20
|
}, "fetching java release");
|
|
34
|
-
let url = `${registryUrl}v3/info/release_versions?page_size=50&image_type=${pkgConfig.imageType}&project=jdk&release_type=ga&sort_method=DATE&sort_order=DESC`;
|
|
35
|
-
if (pkgConfig.architecture) url += `&architecture=${pkgConfig.architecture}`;
|
|
36
|
-
if (pkgConfig.os) url += `&os=${pkgConfig.os}`;
|
|
37
|
-
const result = {
|
|
38
|
-
homepage: "https://adoptium.net",
|
|
39
|
-
releases: []
|
|
40
|
-
};
|
|
41
21
|
try {
|
|
42
|
-
|
|
43
|
-
let releases = await this.getPageReleases(url, page);
|
|
44
|
-
while (releases) {
|
|
45
|
-
result.releases.push(...releases);
|
|
46
|
-
if (releases.length !== 50 || page >= 50) break;
|
|
47
|
-
page += 1;
|
|
48
|
-
releases = await this.getPageReleases(url, page);
|
|
49
|
-
}
|
|
22
|
+
return await getAdoptiumReleases(this.http, pkgConfig);
|
|
50
23
|
} catch (err) {
|
|
51
|
-
if (err instanceof RequestError && err.response?.statusCode !== 404) throw new ExternalHostError(err);
|
|
52
24
|
this.handleGenericErrors(err);
|
|
53
25
|
}
|
|
54
|
-
return result.releases.length ? result : null;
|
|
55
26
|
}
|
|
56
27
|
getReleases(config) {
|
|
57
28
|
return withCache({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../../lib/modules/datasource/java-version/index.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { adoptiumRegistryUrl, getAdoptiumReleases } from './adoptium.ts';\nimport { datasource, parsePackage } from './common.ts';\n\nexport class JavaVersionDatasource extends Datasource {\n static readonly id = datasource;\n\n constructor() {\n super(datasource);\n }\n\n override readonly customRegistrySupport = false;\n\n override readonly defaultRegistryUrls = [adoptiumRegistryUrl];\n\n override readonly caching = true;\n\n private async _getReleases({\n packageName,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n const pkgConfig = parsePackage(packageName);\n logger.trace({ packageName, pkgConfig }, 'fetching java release');\n\n try {\n return await getAdoptiumReleases(this.http, pkgConfig);\n } catch (err) {\n this.handleGenericErrors(err);\n }\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${datasource}`,\n key: `${config.registryUrl}:${config.packageName}`,\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n}\n"],"mappings":";;;;;;AAOA,IAAa,wBAAb,cAA2C,WAAW;CACpD,OAAgB,KAAK;CAErB,cAAc;EACZ,MAAM,UAAU;CAClB;CAEA,wBAA0C;CAE1C,sBAAwC,CAAC,mBAAmB;CAE5D,UAA4B;CAE5B,MAAc,aAAa,EACzB,eACmD;EACnD,MAAM,YAAY,aAAa,WAAW;EAC1C,OAAO,MAAM;GAAE;GAAa;EAAU,GAAG,uBAAuB;EAEhE,IAAI;GACF,OAAO,MAAM,oBAAoB,KAAK,MAAM,SAAS;EACvD,SAAS,KAAK;GACZ,KAAK,oBAAoB,GAAG;EAC9B;CACF;CAEA,YAAY,QAA0D;EACpE,OAAO,UACL;GACE,WAAW,cAAc;GACzB,KAAK,GAAG,OAAO,YAAY,GAAG,OAAO;GACrC,UAAU;EACZ,SACM,KAAK,aAAa,MAAM,CAChC;CACF;AACF"}
|
|
@@ -2,15 +2,19 @@ import { getEnv } from "../../../util/env.js";
|
|
|
2
2
|
import { regEx } from "../../../util/regex.js";
|
|
3
3
|
import { logger } from "../../../logger/index.js";
|
|
4
4
|
import { ensureTrailingSlash, parseUrl } from "../../../util/url.js";
|
|
5
|
-
import { coerceArray } from "../../../util/array.js";
|
|
5
|
+
import { coerceArray, deduplicateArray } from "../../../util/array.js";
|
|
6
|
+
import { ExternalHostError } from "../../../types/errors/external-host-error.js";
|
|
7
|
+
import { Json } from "../../../util/schema-utils/index.js";
|
|
6
8
|
import { id } from "../../versioning/pep440/index.js";
|
|
7
9
|
import { parse } from "../../../util/html.js";
|
|
10
|
+
import { RequestError } from "../../../util/http/got.js";
|
|
11
|
+
import "../../../util/http/index.js";
|
|
8
12
|
import { asTimestamp } from "../../../util/timestamp.js";
|
|
9
13
|
import { Datasource } from "../datasource.js";
|
|
10
14
|
import { getGoogleAuthToken } from "../util.js";
|
|
11
15
|
import { isGitHubRepo, normalizePythonDepName } from "./common.js";
|
|
12
|
-
import { PypiResponse } from "./schema.js";
|
|
13
|
-
import {
|
|
16
|
+
import { PypiResponse, PypiSimpleResponse } from "./schema.js";
|
|
17
|
+
import { isNonEmptyString } from "@sindresorhus/is";
|
|
14
18
|
import changelogFilenameRegex from "changelog-filename-regex";
|
|
15
19
|
//#region lib/modules/datasource/pypi/index.ts
|
|
16
20
|
var PypiDatasource = class PypiDatasource extends Datasource {
|
|
@@ -25,7 +29,7 @@ var PypiDatasource = class PypiDatasource extends Datasource {
|
|
|
25
29
|
defaultVersioning = id;
|
|
26
30
|
registryStrategy = "merge";
|
|
27
31
|
releaseTimestampSupport = true;
|
|
28
|
-
releaseTimestampNote = "The release timestamp is determined from the earliest `upload_time` field of the files of a version.
|
|
32
|
+
releaseTimestampNote = "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).";
|
|
29
33
|
sourceUrlSupport = "release";
|
|
30
34
|
sourceUrlNote = "The source URL is determined from the `homepage` field if it is a github repository, else we use the `project_urls` field.";
|
|
31
35
|
async getReleases({ packageName, registryUrl }) {
|
|
@@ -117,18 +121,7 @@ var PypiDatasource = class PypiDatasource extends Datasource {
|
|
|
117
121
|
"what's new"
|
|
118
122
|
].includes(lower) || changelogFilenameRegex.exec(lower))) dependency.changelogUrl = projectUrl;
|
|
119
123
|
}
|
|
120
|
-
if (dep.releases) dependency.releases =
|
|
121
|
-
const releases = coerceArray(dep.releases?.[version]);
|
|
122
|
-
const isDeprecated = releases.some(({ yanked }) => yanked);
|
|
123
|
-
const result = {
|
|
124
|
-
version,
|
|
125
|
-
releaseTimestamp: PypiDatasource.getEarliestTimestamp(releases)
|
|
126
|
-
};
|
|
127
|
-
if (isDeprecated) result.isDeprecated = isDeprecated;
|
|
128
|
-
const pythonConstraints = releases.map(({ requires_python }) => requires_python).filter(isString);
|
|
129
|
-
result.constraints = { python: Array.from(new Set(pythonConstraints)) };
|
|
130
|
-
return result;
|
|
131
|
-
});
|
|
124
|
+
if (dep.releases) dependency.releases = PypiDatasource.toReleases(dep.releases);
|
|
132
125
|
return dependency;
|
|
133
126
|
}
|
|
134
127
|
static getEarliestTimestamp(releases) {
|
|
@@ -160,38 +153,108 @@ var PypiDatasource = class PypiDatasource extends Datasource {
|
|
|
160
153
|
static cleanSimpleHtml(html) {
|
|
161
154
|
return html.replace(regEx(/<\/?pre>/), "").replace(regEx(/data-requires-python="([^"]*?)>([^"]*?)"/g), "data-requires-python=\"$1>$2\"").replace(regEx(/data-requires-python="([^"]*?)<([^"]*?)"/g), "data-requires-python=\"$1<$2\"");
|
|
162
155
|
}
|
|
156
|
+
static getSimpleReleasesFromHtml(html, packageName) {
|
|
157
|
+
const links = parse(PypiDatasource.cleanSimpleHtml(html)).querySelectorAll("a");
|
|
158
|
+
const releases = {};
|
|
159
|
+
for (const link of Array.from(links)) {
|
|
160
|
+
const version = PypiDatasource.extractVersionFromLinkText(link.text?.trim(), packageName);
|
|
161
|
+
if (version) {
|
|
162
|
+
const release = { yanked: link.hasAttribute("data-yanked") };
|
|
163
|
+
const requiresPython = link.getAttribute("data-requires-python");
|
|
164
|
+
if (requiresPython) release.requires_python = requiresPython;
|
|
165
|
+
(releases[version] ??= []).push(release);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return releases;
|
|
169
|
+
}
|
|
170
|
+
static getSimpleReleasesFromJson(json, packageName) {
|
|
171
|
+
const releases = {};
|
|
172
|
+
const parsed = Json.pipe(PypiSimpleResponse).safeParse(json);
|
|
173
|
+
if (!parsed.success) {
|
|
174
|
+
logger.once.warn({
|
|
175
|
+
packageName,
|
|
176
|
+
err: parsed.error
|
|
177
|
+
}, "Failed to parse JSON-based Simple API response");
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
for (const file of parsed.data.files) {
|
|
181
|
+
const version = PypiDatasource.extractVersionFromLinkText(file.filename, packageName);
|
|
182
|
+
if (version) (releases[version] ??= []).push(file);
|
|
183
|
+
}
|
|
184
|
+
return releases;
|
|
185
|
+
}
|
|
163
186
|
async getSimpleDependency(packageName, hostUrl) {
|
|
164
187
|
const lookupUrl = new URL(ensureTrailingSlash(normalizePythonDepName(packageName)), hostUrl).href;
|
|
165
188
|
const dependency = { releases: [] };
|
|
166
|
-
const { headers, lookupUrl: sanitizedUrl } = await this.getAuthHeaders(lookupUrl);
|
|
167
|
-
const
|
|
189
|
+
const { headers: authHeaders, lookupUrl: sanitizedUrl } = await this.getAuthHeaders(lookupUrl);
|
|
190
|
+
const headers = {
|
|
191
|
+
...authHeaders,
|
|
192
|
+
accept: "application/vnd.pypi.simple.v1+json, application/vnd.pypi.simple.v1+html; q=0.1, text/html; q=0.01"
|
|
193
|
+
};
|
|
194
|
+
let response;
|
|
195
|
+
try {
|
|
196
|
+
response = await this.http.getText(sanitizedUrl, { headers });
|
|
197
|
+
} catch (err) {
|
|
198
|
+
const httpErr = err instanceof ExternalHostError ? err.err : err;
|
|
199
|
+
const statusCode = httpErr instanceof RequestError ? httpErr.response?.statusCode : void 0;
|
|
200
|
+
if (statusCode !== 406) throw err;
|
|
201
|
+
logger.trace({
|
|
202
|
+
packageName,
|
|
203
|
+
hostUrl,
|
|
204
|
+
statusCode
|
|
205
|
+
}, "Registry rejected negotiated Accept header, retrying without it");
|
|
206
|
+
response = await this.http.getText(sanitizedUrl, { headers: authHeaders });
|
|
207
|
+
}
|
|
168
208
|
const dep = response?.body;
|
|
169
209
|
if (!dep) {
|
|
170
210
|
logger.trace({ dependency: packageName }, "pip package not found");
|
|
171
211
|
return null;
|
|
172
212
|
}
|
|
173
213
|
if (response.authorization) dependency.isPrivate = true;
|
|
174
|
-
const
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
releases[version].push(release);
|
|
214
|
+
const contentType = response.headers["content-type"];
|
|
215
|
+
const isJson = !!contentType && contentType.includes("json");
|
|
216
|
+
const looksLikeJson = dep.trimStart().startsWith("{");
|
|
217
|
+
if (isJson) {
|
|
218
|
+
if (looksLikeJson) {
|
|
219
|
+
const releases = PypiDatasource.getSimpleReleasesFromJson(dep, packageName);
|
|
220
|
+
if (!releases) return null;
|
|
221
|
+
dependency.releases = PypiDatasource.toReleases(releases);
|
|
222
|
+
return dependency;
|
|
184
223
|
}
|
|
224
|
+
logger.debug({
|
|
225
|
+
packageName,
|
|
226
|
+
hostUrl,
|
|
227
|
+
contentType
|
|
228
|
+
}, "Parsing Simple API response as HTML, as it is labeled as JSON but does not look like it");
|
|
185
229
|
}
|
|
186
|
-
|
|
230
|
+
const htmlReleases = PypiDatasource.getSimpleReleasesFromHtml(dep, packageName);
|
|
231
|
+
dependency.releases = PypiDatasource.toReleases(htmlReleases);
|
|
232
|
+
if (dependency.releases.length > 0 || !looksLikeJson) return dependency;
|
|
233
|
+
logger.debug({
|
|
234
|
+
packageName,
|
|
235
|
+
hostUrl,
|
|
236
|
+
contentType
|
|
237
|
+
}, "Retrying Simple API response as JSON, as it is not labeled as JSON but looks like it");
|
|
238
|
+
const jsonReleases = PypiDatasource.getSimpleReleasesFromJson(dep, packageName);
|
|
239
|
+
if (!jsonReleases) return null;
|
|
240
|
+
dependency.releases = PypiDatasource.toReleases(jsonReleases);
|
|
241
|
+
return dependency;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* 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.
|
|
245
|
+
*/
|
|
246
|
+
static toReleases(releases) {
|
|
247
|
+
return Object.keys(releases).map((version) => {
|
|
187
248
|
const versionReleases = coerceArray(releases[version]);
|
|
188
249
|
const isDeprecated = versionReleases.some(({ yanked }) => yanked);
|
|
189
250
|
const result = { version };
|
|
251
|
+
const releaseTimestamp = PypiDatasource.getEarliestTimestamp(versionReleases);
|
|
252
|
+
if (releaseTimestamp) result.releaseTimestamp = releaseTimestamp;
|
|
190
253
|
if (isDeprecated) result.isDeprecated = isDeprecated;
|
|
191
|
-
|
|
254
|
+
const pythonConstraints = versionReleases.map(({ requires_python }) => requires_python);
|
|
255
|
+
result.constraints = { python: pythonConstraints.some((constraint) => !isNonEmptyString(constraint)) ? [] : deduplicateArray(pythonConstraints.filter(isNonEmptyString)) };
|
|
192
256
|
return result;
|
|
193
257
|
});
|
|
194
|
-
return dependency;
|
|
195
258
|
}
|
|
196
259
|
};
|
|
197
260
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["pep440.id"],"sources":["../../../../lib/modules/datasource/pypi/index.ts"],"sourcesContent":["import { isString } from '@sindresorhus/is';\nimport changelogFilenameRegex from 'changelog-filename-regex';\nimport { logger } from '../../../logger/index.ts';\nimport { coerceArray } from '../../../util/array.ts';\nimport { getEnv } from '../../../util/env.ts';\nimport { parse } from '../../../util/html.ts';\nimport type { OutgoingHttpHeaders } from '../../../util/http/types.ts';\nimport { regEx } from '../../../util/regex.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 { getGoogleAuthToken } from '../util.ts';\nimport { isGitHubRepo, normalizePythonDepName } from './common.ts';\nimport type { PypiRelease } from './schema.ts';\nimport { PypiResponse } from './schema.ts';\nimport type { Releases } from './types.ts';\n\nexport class PypiDatasource extends Datasource {\n static readonly id = 'pypi';\n\n constructor() {\n super(PypiDatasource.id);\n }\n\n override readonly caching = true;\n\n override readonly customRegistrySupport = true;\n\n static readonly defaultURL =\n getEnv().PIP_INDEX_URL ?? 'https://pypi.org/pypi/';\n override readonly defaultRegistryUrls = [PypiDatasource.defaultURL];\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. This field is not available when using the simple API.';\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 if (parsedUrl.hostname.endsWith('.pkg.dev')) {\n const auth = await getGoogleAuthToken();\n if (auth) {\n const sanitizedLookupUrl = this.sanitizeLookupUrl(lookupUrl, parsedUrl);\n return {\n headers: { authorization: `Basic ${auth}` },\n lookupUrl: sanitizedLookupUrl,\n };\n }\n logger.once.debug({ lookupUrl }, 'Could not get Google access token');\n return { headers: {}, lookupUrl };\n }\n return { headers: {}, lookupUrl };\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 const versions = Object.keys(dep.releases);\n dependency.releases = versions.map((version) => {\n const releases = coerceArray(dep.releases?.[version]);\n const isDeprecated = releases.some(({ yanked }) => yanked);\n const result: Release = {\n version,\n releaseTimestamp: PypiDatasource.getEarliestTimestamp(releases),\n };\n if (isDeprecated) {\n result.isDeprecated = isDeprecated;\n }\n // There may be multiple releases with different requires_python, so we return all in an array\n const pythonConstraints = releases\n .map(({ requires_python }) => requires_python)\n .filter(isString);\n result.constraints = {\n python: Array.from(new Set(pythonConstraints)),\n };\n return result;\n });\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=\"([^\"]*?)>([^\"]*?)\"/g),\n 'data-requires-python=\"$1>$2\"',\n )\n .replace(\n regEx(/data-requires-python=\"([^\"]*?)<([^\"]*?)\"/g),\n 'data-requires-python=\"$1<$2\"',\n )\n );\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, lookupUrl: sanitizedUrl } =\n await this.getAuthHeaders(lookupUrl);\n const response = await this.http.getText(sanitizedUrl, { headers });\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 const root = parse(PypiDatasource.cleanSimpleHtml(dep));\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 if (!releases[version]) {\n releases[version] = [];\n }\n releases[version].push(release);\n }\n }\n const versions = Object.keys(releases);\n dependency.releases = versions.map((version) => {\n const versionReleases = coerceArray(releases[version]);\n const isDeprecated = versionReleases.some(({ yanked }) => yanked);\n const result: Release = { version };\n if (isDeprecated) {\n result.isDeprecated = isDeprecated;\n }\n // There may be multiple releases with different requires_python, so we return all in an array\n result.constraints = {\n // TODO: string[] isn't allowed here\n python: versionReleases.map(\n ({ requires_python }) => requires_python,\n ) as any,\n };\n return result;\n });\n return dependency;\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 type {\n HttpResponse,\n OutgoingHttpHeaders,\n} from '../../../util/http/types.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { Json } from '../../../util/schema-utils/index.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 { getGoogleAuthToken } from '../util.ts';\nimport { isGitHubRepo, normalizePythonDepName } from './common.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 = 'pypi';\n\n constructor() {\n super(PypiDatasource.id);\n }\n\n override readonly caching = true;\n\n override readonly customRegistrySupport = true;\n\n static readonly defaultURL =\n getEnv().PIP_INDEX_URL ?? 'https://pypi.org/pypi/';\n override readonly defaultRegistryUrls = [PypiDatasource.defaultURL];\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 if (parsedUrl.hostname.endsWith('.pkg.dev')) {\n const auth = await getGoogleAuthToken();\n if (auth) {\n const sanitizedLookupUrl = this.sanitizeLookupUrl(lookupUrl, parsedUrl);\n return {\n headers: { authorization: `Basic ${auth}` },\n lookupUrl: sanitizedLookupUrl,\n };\n }\n logger.once.debug({ lookupUrl }, 'Could not get Google access token');\n return { headers: {}, lookupUrl };\n }\n return { headers: {}, lookupUrl };\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=\"([^\"]*?)>([^\"]*?)\"/g),\n 'data-requires-python=\"$1>$2\"',\n )\n .replace(\n regEx(/data-requires-python=\"([^\"]*?)<([^\"]*?)\"/g),\n 'data-requires-python=\"$1<$2\"',\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":";;;;;;;;;;;;;;;;;;;AA0BA,IAAa,iBAAb,MAAa,uBAAuB,WAAW;CAC7C,OAAgB,KAAK;CAErB,cAAc;EACZ,MAAM,eAAe,EAAE;CACzB;CAEA,UAA4B;CAE5B,wBAA0C;CAE1C,OAAgB,aACd,OAAO,CAAC,CAAC,iBAAiB;CAC5B,sBAAwC,CAAC,eAAe,UAAU;CAElE,oBAAsCA;CAEtC,mBAAqC;CAErC,0BAA4C;CAC5C,uBACE;CACF,mBAAqC;CACrC,gBACE;CAEF,MAAM,YAAY,EAChB,aACA,eACmD;EACnD,IAAI,aAAmC;EAEvC,MAAM,UAAU,oBACd,YAAa,QAAQ,2BAA2B,uBAAuB,CACzE;EACA,MAAM,uBAAuB,uBAAuB,WAAW;EAG/D,IAAI,QAAQ,SAAS,UAAU,KAAK,QAAQ,SAAS,WAAW,GAAG;GACjE,OAAO,MACL;IAAE;IAAa;GAAQ,GACvB,mCACF;GACA,aAAa,MAAM,KAAK,oBACtB,sBACA,OACF;EACF,OAAO;GACL,OAAO,MAAM;IAAE;IAAa;GAAQ,GAAG,gCAAgC;GACvE,IAAI;IAEF,aAAa,MAAM,KAAK,cAAc,sBAAsB,OAAO;GACrE,SAAS,KAAK;IAEZ,OAAO,MACL;KAAE;KAAa;KAAS;IAAI,GAC5B,gDACF;IACA,aAAa,MAAM,KAAK,oBACtB,sBACA,OACF;GACF;EACF;EACA,OAAO;CACT;CAEA,kBAA0B,WAAmB,WAAwB;EACnE,IAAI,CAAC,UAAU,YAAY,CAAC,UAAU,UACpC,OAAO;EAGT,UAAU,WAAW;EACrB,UAAU,WAAW;EACrB,OAAO,UAAU,SAAS;CAC5B;CAEA,MAAc,eACZ,WAC8D;EAC9D,MAAM,YAAY,SAAS,SAAS;;EAEpC,IAAI,CAAC,WAAW;GACd,OAAO,KAAK,MAAM,EAAE,UAAU,GAAG,qBAAqB;GACtD,OAAO;IAAE,SAAS,CAAC;IAAG;GAAU;EAClC;EACA,IAAI,UAAU,SAAS,SAAS,UAAU,GAAG;GAC3C,MAAM,OAAO,MAAM,mBAAmB;GACtC,IAAI,MAAM;IACR,MAAM,qBAAqB,KAAK,kBAAkB,WAAW,SAAS;IACtE,OAAO;KACL,SAAS,EAAE,eAAe,SAAS,OAAO;KAC1C,WAAW;IACb;GACF;GACA,OAAO,KAAK,MAAM,EAAE,UAAU,GAAG,mCAAmC;GACpE,OAAO;IAAE,SAAS,CAAC;IAAG;GAAU;EAClC;EACA,OAAO;GAAE,SAAS,CAAC;GAAG;EAAU;CAClC;CAEA,MAAc,cACZ,aACA,SAC+B;EAC/B,MAAM,YAAY,IAAI,IACpB,GAAG,uBAAuB,WAAW,EAAE,QACvC,OACF,CAAC,CAAC;EACF,MAAM,aAA4B,EAAE,UAAU,CAAC,EAAE;EACjD,OAAO,MAAM,EAAE,UAAU,GAAG,qBAAqB;EACjD,MAAM,EAAE,SAAS,WAAW,iBAC1B,MAAM,KAAK,eAAe,SAAS;EACrC,MAAM,MAAM,MAAM,KAAK,KAAK,QAC1B,cACA,EAAE,QAAQ,GACV,YACF;EACA,MAAM,MAAM,KAAK;EACjB,IAAI,IAAI,eACN,WAAW,YAAY;EAEzB,OAAO,MAAM,EAAE,UAAU,GAAG,qBAAqB;EAEjD,IAAI,IAAI,MAAM,WAAW;GACvB,WAAW,WAAW,IAAI,KAAK;GAC/B,IAAI,aAAa,IAAI,KAAK,SAAS,GACjC,WAAW,YAAY,IAAI,KAAK,UAAU,QACxC,WACA,UACF;EAEJ;EAEA,IAAI,IAAI,MAAM,cACZ,KAAK,MAAM,CAAC,MAAM,eAAe,OAAO,QAAQ,IAAI,KAAK,YAAY,GAAG;GACtE,MAAM,QAAQ,KAAK,YAAY;GAE/B,IACE,cACA,CAAC,WAAW,cACX,MAAM,WAAW,MAAM,KACtB,UAAU,UACV,UAAU,YACV,aAAa,UAAU,IAEzB,WAAW,YAAY;GAGzB,IACE,CAAC,WAAW,iBACX;IACC;IACA;IACA;IACA;IACA;IACA;GACF,CAAC,CAAC,SAAS,KAAK,KACd,uBAAuB,KAAK,KAAK,IAGnC,WAAW,eAAe;EAE9B;EAGF,IAAI,IAAI,UACN,WAAW,WAAW,eAAe,WAAW,IAAI,QAAQ;EAE9D,OAAO;CACT;CAEA,OAAe,qBACb,UACkB;EAClB,IAAI,WAA6B;EACjC,KAAK,MAAM,EAAE,iBAAiB,UAAU;GACtC,MAAM,YAAY,YAAY,WAAW;GAEzC,IAAI,cAAc,CAAC,YAAY,YAAY,WACzC,WAAW;EAEf;EACA,OAAO;CACT;CAEA,OAAe,2BACb,MACA,aACe;EAEf,MAAM,SAAS,KAAK,YAAY;EAChC,MAAM,oBAAoB,uBAAuB,IAAI;EACrD,MAAM,YAAY,GAAG,YAAY;EAOjC,IAAI,CAAC,kBAAkB,WAAW,SAAS,GACzC,OAAO;EAMT,MAAM,uBAAuB,OAAO,SAAS,kBAAkB;EAC/D,MAAM,MAAM,OAAO,MAAM,UAAU,SAAS,oBAAoB;EAIhE,MAAM,YAAY;GADG;GAAW;GAAY;GAAW;GAAQ;EACnC,CAAC,CAAC,MAAM,WAAW,OAAO,SAAS,MAAM,CAAC;EACtE,IAAI,WAEF,OAAO,IAAI,MAAM,GAAG,CAAC,UAAU,MAAM;EAMvC,IAAI,OAAO,SAAS,MAAW,KAAK,OAAO,MAAM,GAAG,CAAC,CAAC,SAAS,GAC7D,OAAO,IAAI,MAAM,GAAG,CAAC,CAAC;EAExB,OAAO;CACT;CAEA,OAAe,gBAAgB,MAAsB;EACnD,OACE,KACG,QAAQ,MAAM,UAAU,GAAG,EAAE,CAAC,CAE9B,QACC,MAAM,2CAA2C,GACjD,mCACF,CAAC,CACA,QACC,MAAM,2CAA2C,GACjD,mCACF;CAEN;CAEA,OAAe,0BACb,MACA,aACU;EAEV,MAAM,QADO,MAAM,eAAe,gBAAgB,IAAI,CACrC,CAAC,CAAC,iBAAiB,GAAG;EACvC,MAAM,WAAqB,CAAC;EAC5B,KAAK,MAAM,QAAQ,MAAM,KAAK,KAAK,GAAG;GACpC,MAAM,UAAU,eAAe,2BAC7B,KAAK,MAAM,KAAK,GAChB,WACF;GACA,IAAI,SAAS;IACX,MAAM,UAAuB,EAC3B,QAAQ,KAAK,aAAa,aAAa,EACzC;IACA,MAAM,iBAAiB,KAAK,aAAa,sBAAsB;IAC/D,IAAI,gBACF,QAAQ,kBAAkB;IAE5B,CAAC,SAAS,aAAa,CAAC,EAAA,CAAG,KAAK,OAAO;GACzC;EACF;EACA,OAAO;CACT;CAEA,OAAe,0BACb,MACA,aACiB;EACjB,MAAM,WAAqB,CAAC;EAC5B,MAAM,SAAS,KAAK,KAAK,kBAAkB,CAAC,CAAC,UAAU,IAAI;EAC3D,IAAI,CAAC,OAAO,SAAS;GACnB,OAAO,KAAK,KACV;IAAE;IAAa,KAAK,OAAO;GAAM,GACjC,gDACF;GAEA,OAAO;EACT;EACA,KAAK,MAAM,QAAQ,OAAO,KAAK,OAAO;GACpC,MAAM,UAAU,eAAe,2BAC7B,KAAK,UACL,WACF;GACA,IAAI,SACF,CAAC,SAAS,aAAa,CAAC,EAAA,CAAG,KAAK,IAAI;EAExC;EACA,OAAO;CACT;CAEA,MAAc,oBACZ,aACA,SAC+B;EAC/B,MAAM,YAAY,IAAI,IACpB,oBAAoB,uBAAuB,WAAW,CAAC,GACvD,OACF,CAAC,CAAC;EACF,MAAM,aAA4B,EAAE,UAAU,CAAC,EAAE;EACjD,MAAM,EAAE,SAAS,aAAa,WAAW,iBACvC,MAAM,KAAK,eAAe,SAAS;EACrC,MAAM,UAA+B;GACnC,GAAG;GAGH,QACE;EACJ;EACA,IAAI;EACJ,IAAI;GACF,WAAW,MAAM,KAAK,KAAK,QAAQ,cAAc,EAAE,QAAQ,CAAC;EAC9D,SAAS,KAAK;GAEZ,MAAM,UAAU,eAAe,oBAAoB,IAAI,MAAM;GAC7D,MAAM,aACJ,mBAAmBC,eAAY,QAAQ,UAAU,aAAa,KAAA;GAIhE,IAAI,eAAe,KACjB,MAAM;GAGR,OAAO,MACL;IAAE;IAAa;IAAS;GAAW,GACnC,iEACF;GACA,WAAW,MAAM,KAAK,KAAK,QAAQ,cAAc,EAC/C,SAAS,YACX,CAAC;EACH;EACA,MAAM,MAAM,UAAU;EACtB,IAAI,CAAC,KAAK;GACR,OAAO,MAAM,EAAE,YAAY,YAAY,GAAG,uBAAuB;GACjE,OAAO;EACT;EACA,IAAI,SAAS,eACX,WAAW,YAAY;EAIzB,MAAM,cAAc,SAAS,QAAQ;EACrC,MAAM,SAAS,CAAC,CAAC,eAAe,YAAY,SAAS,MAAM;EAC3D,MAAM,gBAAgB,IAAI,UAAU,CAAC,CAAC,WAAW,GAAG;EAEpD,IAAI,QAAQ;GACV,IAAI,eAAe;IACjB,MAAM,WAAW,eAAe,0BAC9B,KACA,WACF;IAEA,IAAI,CAAC,UACH,OAAO;IAET,WAAW,WAAW,eAAe,WAAW,QAAQ;IACxD,OAAO;GACT;GAGA,OAAO,MACL;IAAE;IAAa;IAAS;GAAY,GACpC,yFACF;EACF;EAEA,MAAM,eAAe,eAAe,0BAClC,KACA,WACF;EACA,WAAW,WAAW,eAAe,WAAW,YAAY;EAC5D,IAAI,WAAW,SAAS,SAAS,KAAK,CAAC,eACrC,OAAO;EAIT,OAAO,MACL;GAAE;GAAa;GAAS;EAAY,GACpC,sFACF;EACA,MAAM,eAAe,eAAe,0BAClC,KACA,WACF;EACA,IAAI,CAAC,cACH,OAAO;EAET,WAAW,WAAW,eAAe,WAAW,YAAY;EAC5D,OAAO;CACT;;;;CAKA,OAAe,WAAW,UAA+B;EAEvD,OADiB,OAAO,KAAK,QACf,CAAC,CAAC,KAAK,YAAY;GAC/B,MAAM,kBAAkB,YAAY,SAAS,QAAQ;GACrD,MAAM,eAAe,gBAAgB,MAAM,EAAE,aAAa,MAAM;GAChE,MAAM,SAAkB,EAAE,QAAQ;GAClC,MAAM,mBACJ,eAAe,qBAAqB,eAAe;GACrD,IAAI,kBACF,OAAO,mBAAmB;GAE5B,IAAI,cACF,OAAO,eAAe;GAExB,MAAM,oBAAoB,gBAAgB,KACvC,EAAE,sBAAsB,eAC3B;GAOA,OAAO,cAAc,EACnB,QANsB,kBAAkB,MACvC,eAAe,CAAC,iBAAiB,UAAU,CAKtB,IAClB,CAAC,IACD,iBAAiB,kBAAkB,OAAO,gBAAgB,CAAC,EACjE;GACA,OAAO;EACT,CAAC;CACH;AACF"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { DeepNullish } from "../../../util/schema-utils/index.js";
|
|
1
|
+
import { DeepNullish, LooseArray } from "../../../util/schema-utils/index.js";
|
|
2
|
+
import { isTruthy } from "@sindresorhus/is";
|
|
2
3
|
import { z } from "zod/v4";
|
|
3
4
|
//#region lib/modules/datasource/pypi/schema.ts
|
|
4
5
|
const PypiRelease = DeepNullish(z.object({
|
|
@@ -14,7 +15,19 @@ const PypiResponse = DeepNullish(z.object({
|
|
|
14
15
|
}).optional(),
|
|
15
16
|
releases: z.record(z.string(), z.array(PypiRelease)).optional()
|
|
16
17
|
}));
|
|
18
|
+
const PypiSimpleFile = DeepNullish(z.object({
|
|
19
|
+
filename: z.string(),
|
|
20
|
+
"requires-python": z.string().optional(),
|
|
21
|
+
yanked: z.union([z.boolean(), z.string()]).optional().default(false),
|
|
22
|
+
"upload-time": z.string().optional()
|
|
23
|
+
})).transform(({ filename, "requires-python": requires_python, yanked, "upload-time": upload_time }) => ({
|
|
24
|
+
filename,
|
|
25
|
+
requires_python,
|
|
26
|
+
yanked: isTruthy(yanked),
|
|
27
|
+
upload_time
|
|
28
|
+
}));
|
|
29
|
+
const PypiSimpleResponse = z.object({ files: LooseArray(PypiSimpleFile) });
|
|
17
30
|
//#endregion
|
|
18
|
-
export { PypiRelease, PypiResponse };
|
|
31
|
+
export { PypiRelease, PypiResponse, PypiSimpleFile, PypiSimpleResponse };
|
|
19
32
|
|
|
20
33
|
//# sourceMappingURL=schema.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","names":[],"sources":["../../../../lib/modules/datasource/pypi/schema.ts"],"sourcesContent":["import { z } from 'zod/v4';\nimport { DeepNullish } from '../../../util/schema-utils/index.ts';\n\nexport const PypiRelease = DeepNullish(\n z.object({\n requires_python: z.string().optional(),\n upload_time: z.string().optional(),\n yanked: z.boolean().optional().default(false),\n }),\n);\n\nexport type PypiRelease = z.infer<typeof PypiRelease>;\n\nexport const PypiResponse = DeepNullish(\n z.object({\n info: z\n .object({\n name: z.string().optional(),\n home_page: z.string().optional(),\n project_urls: z.record(z.string(), z.string().optional()).optional(),\n })\n .optional(),\n releases: z.record(z.string(), z.array(PypiRelease)).optional(),\n }),\n);\n\nexport type PypiResponse = z.infer<typeof PypiResponse>;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"schema.js","names":[],"sources":["../../../../lib/modules/datasource/pypi/schema.ts"],"sourcesContent":["import { isTruthy } from '@sindresorhus/is';\nimport { z } from 'zod/v4';\nimport { DeepNullish, LooseArray } from '../../../util/schema-utils/index.ts';\n\nexport const PypiRelease = DeepNullish(\n z.object({\n requires_python: z.string().optional(),\n upload_time: z.string().optional(),\n yanked: z.boolean().optional().default(false),\n }),\n);\n\nexport type PypiRelease = z.infer<typeof PypiRelease>;\n\nexport const PypiResponse = DeepNullish(\n z.object({\n info: z\n .object({\n name: z.string().optional(),\n home_page: z.string().optional(),\n project_urls: z.record(z.string(), z.string().optional()).optional(),\n })\n .optional(),\n releases: z.record(z.string(), z.array(PypiRelease)).optional(),\n }),\n);\n\nexport type PypiResponse = z.infer<typeof PypiResponse>;\n\n// JSON-based Simple API (PEP 691) schemas\n// https://peps.python.org/pep-0691/\nexport const PypiSimpleFile = DeepNullish(\n z.object({\n filename: z.string(),\n 'requires-python': z.string().optional(),\n yanked: z.union([z.boolean(), z.string()]).optional().default(false),\n // `upload-time` is specified by PEP 700, which mandates an ISO 8601 string and lets a server omit the key even at `api-version` 1.1.\n // A string which `asTimestamp` cannot parse downstream degrades to a missing timestamp; a value of any other type fails validation, and `LooseArray` then drops the file entry.\n 'upload-time': z.string().optional(),\n }),\n).transform(\n ({\n filename,\n 'requires-python': requires_python,\n yanked,\n 'upload-time': upload_time,\n }) => ({\n filename,\n requires_python,\n // Per the Simple API spec, `yanked` is yanked when truthy\n // https://packaging.python.org/en/latest/specifications/simple-repository-api/#json-based-simple-api-for-python-package-indexes\n yanked: isTruthy(yanked),\n upload_time,\n }),\n);\nexport type PypiSimpleFile = z.infer<typeof PypiSimpleFile>;\n\nexport const PypiSimpleResponse = z.object({\n files: LooseArray(PypiSimpleFile),\n});\nexport type PypiSimpleResponse = z.infer<typeof PypiSimpleResponse>;\n"],"mappings":";;;;AAIA,MAAa,cAAc,YACzB,EAAE,OAAO;CACP,iBAAiB,EAAE,OAAO,CAAC,CAAC,SAAS;CACrC,aAAa,EAAE,OAAO,CAAC,CAAC,SAAS;CACjC,QAAQ,EAAE,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,QAAQ,KAAK;AAC9C,CAAC,CACH;AAIA,MAAa,eAAe,YAC1B,EAAE,OAAO;CACP,MAAM,EACH,OAAO;EACN,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS;EAC1B,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS;EAC/B,cAAc,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;CACrE,CAAC,CAAC,CACD,SAAS;CACZ,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,MAAM,WAAW,CAAC,CAAC,CAAC,SAAS;AAChE,CAAC,CACH;AAMA,MAAa,iBAAiB,YAC5B,EAAE,OAAO;CACP,UAAU,EAAE,OAAO;CACnB,mBAAmB,EAAE,OAAO,CAAC,CAAC,SAAS;CACvC,QAAQ,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,QAAQ,KAAK;CAGnE,eAAe,EAAE,OAAO,CAAC,CAAC,SAAS;AACrC,CAAC,CACH,CAAC,CAAC,WACC,EACC,UACA,mBAAmB,iBACnB,QACA,eAAe,mBACV;CACL;CACA;CAGA,QAAQ,SAAS,MAAM;CACvB;AACF,EACF;AAGA,MAAa,qBAAqB,EAAE,OAAO,EACzC,OAAO,WAAW,cAAc,EAClC,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { regEx } from "../../../util/regex.js";
|
|
2
2
|
//#region lib/modules/manager/ant/properties.ts
|
|
3
3
|
const fullPlaceholderRegex = regEx(/^\$\{([^}]+)}$/);
|
|
4
4
|
const placeholderTestRegex = regEx(/\$\{[^}]+}/);
|
|
@@ -14,7 +14,7 @@ function findAttrValuePosition(content, node, attrName) {
|
|
|
14
14
|
const startTag = node.startTagPosition;
|
|
15
15
|
const tagEnd = content.indexOf(">", startTag);
|
|
16
16
|
const tagContent = content.slice(startTag, tagEnd + 1);
|
|
17
|
-
const match = regEx(`${
|
|
17
|
+
const match = regEx(`${RegExp.escape(attrName)}\\s*=\\s*(?:"([^"]*)"|'([^']*)')`).exec(tagContent);
|
|
18
18
|
const valueInMatch = match[1] ?? match[2];
|
|
19
19
|
const valueOffset = match[0].indexOf(valueInMatch);
|
|
20
20
|
return startTag + match.index + valueOffset;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"properties.js","names":[],"sources":["../../../../lib/modules/manager/ant/properties.ts"],"sourcesContent":["import type { XmlElement } from 'xmldoc';\nimport {
|
|
1
|
+
{"version":3,"file":"properties.js","names":[],"sources":["../../../../lib/modules/manager/ant/properties.ts"],"sourcesContent":["import type { XmlElement } from 'xmldoc';\nimport { regEx } from '../../../util/regex.ts';\nimport type { PackageDependency } from '../types.ts';\nimport type { AntProp } from './types.ts';\n\nconst fullPlaceholderRegex = regEx(/^\\$\\{([^}]+)}$/);\nconst placeholderTestRegex = regEx(/\\$\\{[^}]+}/);\nconst propertySeparatorRegex = regEx(/^([^=:\\s]+)\\s*[=:\\s]\\s*(.*)$/);\n\nexport function containsPlaceholder(str: string | null | undefined): boolean {\n return !!str && placeholderTestRegex.test(str);\n}\n\n/**\n * Find the byte offset of an attribute's value in raw XML content.\n * Returns the offset of the first character of the value (after the opening quote).\n */\nexport function findAttrValuePosition(\n content: string,\n node: XmlElement,\n attrName: string,\n): number {\n const startTag = node.startTagPosition!;\n const tagEnd = content.indexOf('>', startTag);\n const tagContent = content.slice(startTag, tagEnd + 1);\n\n const attrPattern = regEx(\n `${RegExp.escape(attrName)}\\\\s*=\\\\s*(?:\"([^\"]*)\"|'([^']*)')`,\n );\n const match = attrPattern.exec(tagContent)!;\n\n const valueInMatch = match[1] ?? match[2];\n const valueOffset = match[0].indexOf(valueInMatch);\n return startTag + match.index + valueOffset;\n}\n\n/**\n * Parse a .properties file into a map of property names to AntProp.\n * Implements first-definition-wins: if a key already exists in the map, it is not overwritten.\n */\nexport function parsePropertiesFile(\n content: string,\n packageFile: string,\n props: Record<string, AntProp>,\n): void {\n let offset = 0;\n for (const rawLine of content.split('\\n')) {\n const line = rawLine.trim();\n // Skip comments and blank lines\n if (line.startsWith('#') || line.startsWith('!') || line === '') {\n offset += rawLine.length + 1; // +1 for newline\n continue;\n }\n\n // Match key=value, key:value, or key value (first separator wins)\n const separatorMatch = propertySeparatorRegex.exec(line);\n if (separatorMatch) {\n const key = separatorMatch[1];\n const val = separatorMatch[2].trim();\n\n // First-definition-wins\n if (!(key in props)) {\n // fileReplacePosition points to the start of the value in the raw content\n const lineStart = offset + rawLine.indexOf(line);\n const keyEnd = line.indexOf(separatorMatch[2]);\n const fileReplacePosition = lineStart + keyEnd;\n\n props[key] = { val, fileReplacePosition, packageFile };\n }\n }\n\n offset += rawLine.length + 1;\n }\n}\n\n/**\n * Apply property resolution to a dependency.\n * Handles chained references with circular detection.\n */\nexport function applyProps(\n dep: PackageDependency,\n depPackageFile: string,\n props: Record<string, AntProp>,\n): PackageDependency {\n const currentValue = dep.currentValue;\n\n if (!currentValue || !containsPlaceholder(currentValue)) {\n return dep;\n }\n\n // Check if the entire version is a single property reference\n const fullMatch = fullPlaceholderRegex.exec(currentValue);\n if (!fullMatch) {\n // Partial placeholder in version string - not supported for updates\n dep.skipReason = 'version-placeholder';\n return dep;\n }\n\n const propKey = fullMatch[1];\n const prop = props[propKey];\n if (!prop) {\n dep.skipReason = 'version-placeholder';\n return dep;\n }\n\n // After resolveChainedProps, prop.val is either fully resolved or still contains\n // placeholders (meaning circular or unresolvable)\n if (containsPlaceholder(prop.val)) {\n dep.skipReason = 'recursive-placeholder';\n return dep;\n }\n\n dep.currentValue = prop.val;\n dep.sharedVariableName = propKey;\n dep.fileReplacePosition = prop.fileReplacePosition;\n if (prop.packageFile !== depPackageFile) {\n dep.editFile = prop.packageFile;\n }\n // propSource is used to route deps to the correct PackageFile\n dep.propSource = prop.packageFile;\n return dep;\n}\n\n/**\n * Resolve a single property key, following chained references.\n * Returns the resolved value or null if circular/unresolvable.\n */\nfunction resolveKey(\n key: string,\n props: Record<string, AntProp>,\n resolved: Map<string, string | null>,\n chain: Set<string>,\n): string | null {\n if (resolved.has(key)) {\n return resolved.get(key)!;\n }\n if (chain.has(key)) {\n // Circular reference detected\n resolved.set(key, null);\n return null;\n }\n const prop = props[key];\n if (!prop) {\n return null;\n }\n if (!containsPlaceholder(prop.val)) {\n resolved.set(key, prop.val);\n return prop.val;\n }\n\n chain.add(key);\n let isCircular = false;\n const val = prop.val.replace(\n regEx(/\\$\\{([^}]+)}/g),\n (match, refKey: string) => {\n const refResult = resolveKey(refKey, props, resolved, chain);\n if (refResult === null) {\n isCircular = true;\n return match;\n }\n return refResult;\n },\n );\n chain.delete(key);\n\n if (isCircular) {\n resolved.set(key, null);\n return null;\n }\n\n resolved.set(key, val);\n prop.val = val;\n return val;\n}\n\n/**\n * Resolve chained property references within the property map itself.\n * E.g., if prop A = \"${B}\" and prop B = \"1.0\", resolve A to \"1.0\".\n * Marks circular properties by setting val to a placeholder that will be caught later.\n */\nexport function resolveChainedProps(props: Record<string, AntProp>): void {\n const resolved = new Map<string, string | null>(); // null = circular\n\n for (const key of Object.keys(props)) {\n resolveKey(key, props, resolved, new Set());\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { regEx } from "../../../../util/regex.js";
|
|
2
2
|
import { parseUrl } from "../../../../util/url.js";
|
|
3
3
|
import { GithubReleasesDatasource } from "../../../datasource/github-releases/index.js";
|
|
4
4
|
import { GithubTagsDatasource } from "../../../datasource/github-tags/index.js";
|
|
@@ -27,7 +27,7 @@ const archiveSuffixRegex = regEx(`(?:${[
|
|
|
27
27
|
".zst",
|
|
28
28
|
".tar.zst",
|
|
29
29
|
".tzst"
|
|
30
|
-
].map(
|
|
30
|
+
].map((archive) => RegExp.escape(archive)).join("|")})$`);
|
|
31
31
|
function stripArchiveSuffix(value) {
|
|
32
32
|
return value.replace(archiveSuffixRegex, "");
|
|
33
33
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","names":[],"sources":["../../../../../lib/modules/manager/bazel/rules/http.ts"],"sourcesContent":["import { isString, isTruthy } from '@sindresorhus/is';\nimport { z } from 'zod/v4';\nimport {
|
|
1
|
+
{"version":3,"file":"http.js","names":[],"sources":["../../../../../lib/modules/manager/bazel/rules/http.ts"],"sourcesContent":["import { isString, isTruthy } from '@sindresorhus/is';\nimport { z } from 'zod/v4';\nimport { regEx } from '../../../../util/regex.ts';\nimport { parseUrl } from '../../../../util/url.ts';\nimport { GithubReleasesDatasource } from '../../../datasource/github-releases/index.ts';\nimport { GithubTagsDatasource } from '../../../datasource/github-tags/index.ts';\nimport { GitlabReleasesDatasource } from '../../../datasource/gitlab-releases/index.ts';\nimport { GitlabTagsDatasource } from '../../../datasource/gitlab-tags/index.ts';\nimport type { PackageDependency } from '../../types.ts';\n\n// Source: https://bazel.build/rules/lib/repo/http\nconst archives = [\n '.zip',\n '.tar',\n '.jar',\n '.war',\n '.aar',\n '.ar',\n '.deb',\n\n '.gz',\n '.tar.gz',\n '.tgz',\n\n '.bz2',\n '.tar.bz2',\n '.tbz2',\n\n '.xz',\n '.tar.xz',\n '.txz',\n\n '.zst',\n '.tar.zst',\n '.tzst',\n];\n\nconst archiveSuffixRegex = regEx(\n `(?:${archives.map((archive) => RegExp.escape(archive)).join('|')})$`,\n);\n\nfunction stripArchiveSuffix(value: string): string {\n return value.replace(archiveSuffixRegex, '');\n}\n\nfunction isHash(value: unknown): value is string {\n return isString(value) && regEx(/[0-9a-z]{40}/i).test(value);\n}\n\nexport function parseGithubPath(\n pathname: string,\n): Partial<PackageDependency> | null {\n const [p0, p1, p2, p3, p4, p5] = pathname.split('/').slice(1);\n const packageName = `${p0}/${p1}`;\n let datasource = '';\n let value: string | null = null;\n if (p2 === 'releases' && p3 === 'download') {\n datasource = GithubReleasesDatasource.id;\n value = p4;\n } else if (p2 === 'archive' && p3 === 'refs' && p4 === 'tags') {\n // https://github.com/foo/bar/archive/refs/tags/v1.2.3.tar.gz\n datasource = GithubTagsDatasource.id;\n value = p5;\n } else if (p2 === 'archive') {\n // https://github.com/foo/bar/archive/1.2.3.tar.gz\n datasource = GithubTagsDatasource.id;\n value = p3;\n }\n\n if (!value) {\n return null;\n }\n\n value = stripArchiveSuffix(value);\n return isHash(value)\n ? { datasource, packageName, currentDigest: value }\n : { datasource, packageName, currentValue: value };\n}\n\nfunction parseGitlabPath(pathname: string): Partial<PackageDependency> | null {\n // https://gitlab.com/libeigen/eigen/-/archive/3.3.5/eigen-3.3.5.zip\n // https://gitlab.com/libeigen/eigen/-/archive/90ee821c563fa20db4d64d6991ddca256d5c52f2/eigen-90ee821c563fa20db4d64d6991ddca256d5c52f2.tar.gz\n const [p0, p1, p2, p3, p4] = pathname.split('/').slice(1);\n const packageName = `${p0}/${p1}`;\n if (p2 === '-' && p3 === 'archive' && p4) {\n return isHash(p4)\n ? {\n datasource: GitlabTagsDatasource.id,\n packageName,\n currentDigest: p4,\n }\n : {\n datasource: GitlabReleasesDatasource.id,\n packageName,\n currentValue: p4,\n };\n }\n return null;\n}\n\nexport function parseArchiveUrl(\n urlString: string | undefined | null,\n): Partial<PackageDependency> | null {\n if (!urlString) {\n return null;\n }\n\n const url = parseUrl(urlString);\n\n if (url?.host === 'github.com') {\n return parseGithubPath(url.pathname);\n }\n\n if (url?.host === 'gitlab.com') {\n return parseGitlabPath(url.pathname);\n }\n\n return null;\n}\n\nexport const httpRules = [\n 'http_archive',\n '_http_archive',\n 'http_file',\n '_http_file',\n] as const;\n\nexport const HttpTarget = z\n .object({\n rule: z.enum(httpRules),\n name: z.string(),\n url: z.string().optional(),\n urls: z.array(z.string()).optional(),\n sha256: z.string(),\n })\n .refine(({ url, urls }) => !!url || !!urls)\n .transform(({ rule, name, url, urls = [] }): PackageDependency[] => {\n const parsedUrl = [url, ...urls].map(parseArchiveUrl).find(isTruthy);\n if (!parsedUrl) {\n return [];\n }\n\n const dep: PackageDependency = {\n datasource: parsedUrl.datasource,\n depType: rule,\n depName: name,\n packageName: parsedUrl.packageName,\n };\n\n // We don't want to set both `currentValue` and `currentDigest`.\n //\n // What we want is to provide the first occurrence of `currentValue`,\n // or, if it's not available, `currentDigest`.\n //\n // Auto-replace mechanism will replace this first occurrence,\n // and artifact update function will do the rest.\n //\n // Hence, `if-else-if` is being used here.\n if (parsedUrl.currentValue) {\n dep.currentValue = parsedUrl.currentValue;\n } else if (parsedUrl.currentDigest) {\n dep.currentDigest = parsedUrl.currentDigest;\n }\n\n return [dep];\n });\n"],"mappings":";;;;;;;;;AAqCA,MAAM,qBAAqB,MACzB,MAAM;CA1BN;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CAEA;CACA;CACA;CAEA;CACA;CACA;CAEA;CACA;CACA;AAIa,CAAC,CAAC,KAAK,YAAY,OAAO,OAAO,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,GACpE;AAEA,SAAS,mBAAmB,OAAuB;CACjD,OAAO,MAAM,QAAQ,oBAAoB,EAAE;AAC7C;AAEA,SAAS,OAAO,OAAiC;CAC/C,OAAO,SAAS,KAAK,KAAK,MAAM,eAAe,CAAC,CAAC,KAAK,KAAK;AAC7D;AAEA,SAAgB,gBACd,UACmC;CACnC,MAAM,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,MAAM,SAAS,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;CAC5D,MAAM,cAAc,GAAG,GAAG,GAAG;CAC7B,IAAI,aAAa;CACjB,IAAI,QAAuB;CAC3B,IAAI,OAAO,cAAc,OAAO,YAAY;EAC1C,aAAa,yBAAyB;EACtC,QAAQ;CACV,OAAO,IAAI,OAAO,aAAa,OAAO,UAAU,OAAO,QAAQ;EAE7D,aAAa,qBAAqB;EAClC,QAAQ;CACV,OAAO,IAAI,OAAO,WAAW;EAE3B,aAAa,qBAAqB;EAClC,QAAQ;CACV;CAEA,IAAI,CAAC,OACH,OAAO;CAGT,QAAQ,mBAAmB,KAAK;CAChC,OAAO,OAAO,KAAK,IACf;EAAE;EAAY;EAAa,eAAe;CAAM,IAChD;EAAE;EAAY;EAAa,cAAc;CAAM;AACrD;AAEA,SAAS,gBAAgB,UAAqD;CAG5E,MAAM,CAAC,IAAI,IAAI,IAAI,IAAI,MAAM,SAAS,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;CACxD,MAAM,cAAc,GAAG,GAAG,GAAG;CAC7B,IAAI,OAAO,OAAO,OAAO,aAAa,IACpC,OAAO,OAAO,EAAE,IACZ;EACE,YAAY,qBAAqB;EACjC;EACA,eAAe;CACjB,IACA;EACE,YAAY,yBAAyB;EACrC;EACA,cAAc;CAChB;CAEN,OAAO;AACT;AAEA,SAAgB,gBACd,WACmC;CACnC,IAAI,CAAC,WACH,OAAO;CAGT,MAAM,MAAM,SAAS,SAAS;CAE9B,IAAI,KAAK,SAAS,cAChB,OAAO,gBAAgB,IAAI,QAAQ;CAGrC,IAAI,KAAK,SAAS,cAChB,OAAO,gBAAgB,IAAI,QAAQ;CAGrC,OAAO;AACT;AAEA,MAAa,YAAY;CACvB;CACA;CACA;CACA;AACF;AAEA,MAAa,aAAa,EACvB,OAAO;CACN,MAAM,EAAE,KAAK,SAAS;CACtB,MAAM,EAAE,OAAO;CACf,KAAK,EAAE,OAAO,CAAC,CAAC,SAAS;CACzB,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS;CACnC,QAAQ,EAAE,OAAO;AACnB,CAAC,CAAC,CACD,QAAQ,EAAE,KAAK,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAC1C,WAAW,EAAE,MAAM,MAAM,KAAK,OAAO,CAAC,QAA6B;CAClE,MAAM,YAAY,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,IAAI,eAAe,CAAC,CAAC,KAAK,QAAQ;CACnE,IAAI,CAAC,WACH,OAAO,CAAC;CAGV,MAAM,MAAyB;EAC7B,YAAY,UAAU;EACtB,SAAS;EACT,SAAS;EACT,aAAa,UAAU;CACzB;CAWA,IAAI,UAAU,cACZ,IAAI,eAAe,UAAU;MACxB,IAAI,UAAU,eACnB,IAAI,gBAAgB,UAAU;CAGhC,OAAO,CAAC,GAAG;AACb,CAAC"}
|