{"version":3,"file":"pypi.mjs","names":[],"sources":["../../src/registries/pypi.ts"],"sourcesContent":["import type { Client } from \"../core/client.ts\";\nimport type { Dependency, Maintainer, Package, URLBuilder, Version } from \"../core/types.ts\";\nimport { Registry } from \"../core/registry.ts\";\nimport { normalizeLicense } from \"../core/license.ts\";\nimport { normalizeRepositoryURL } from \"../core/repository.ts\";\nimport { buildPURL } from \"../core/purl.ts\";\nimport { rethrowFetchError } from \"../core/errors.ts\";\n\nconst PYPI_FILENAME_SEPARATORS = [\"_\", \"-\", \".\"] as const;\n\n/** PyPI JSON API response for a package. */\ninterface PyPIPackageResponse {\n  info: {\n    name: string;\n    version: string;\n    summary: string;\n    description: string;\n    license: string | null;\n    license_expression?: string | null;\n    keywords: string;\n    author: string | null;\n    author_email: string | null;\n    maintainer?: string | null;\n    maintainer_email?: string | null;\n    project_urls: Record<string, string>;\n    requires_dist: string[] | null;\n  };\n  urls: PyPIFile[];\n}\n\n/** PyPI file information. */\ninterface PyPIFile {\n  filename: string;\n  url: string;\n  upload_time_iso_8601: string;\n  yanked: boolean;\n  digests: {\n    sha256: string;\n  };\n}\n\n/** PyPI Simple API (PEP 691) response for a project. */\ninterface PyPISimpleResponse {\n  meta: { \"api-version\": string };\n  name: string;\n  versions: string[];\n  files: PyPISimpleFile[];\n}\n\n/** A single file entry in the Simple API response. */\ninterface PyPISimpleFile {\n  readonly filename: string;\n  readonly url: string;\n  readonly hashes: Readonly<{ sha256?: string }>;\n  readonly \"requires-python\"?: string;\n  readonly yanked?: string | false;\n  readonly \"upload-time\"?: string;\n}\n\n/**\n * PEP 639 projects put the SPDX expression in `license_expression` and leave `license` null.\n *\n * @param info - License fields of the JSON API `info` object.\n * @returns {string} The normalized SPDX expression, or an empty string.\n */\nfunction pypiLicense(\n  info: Readonly<Pick<PyPIPackageResponse[\"info\"], \"license\" | \"license_expression\">>,\n): string {\n  return normalizeLicense(info.license_expression || info.license);\n}\n\ninterface PyPIContact {\n  readonly name: string;\n  readonly email: string;\n}\n\nconst QUOTED_NAME_PATTERN = /^\"((?:[^\"\\\\]|\\\\.)*)\"\\s*<([^>]*)>$/;\nconst ANGLE_ADDRESS_PATTERN = /^([^<]*?)\\s*<([^>]*)>$/;\nconst COMMENT_PATTERN = /\\(([^)]*)\\)/g;\nconst ADDRESS_ITEM_PATTERN = /(?:\"(?:[^\"\\\\]|\\\\.)*\"|<[^>]*>|\\([^)]*\\)|[^,])+/g;\n\n/**\n * Split an address list on the commas outside quotes, angle brackets and one level of comments.\n *\n * @param value - Comma separated mailboxes.\n * @returns {string[]} The trimmed, non-empty items.\n */\nfunction splitAddressList(value: string): string[] {\n  return [...value.matchAll(ADDRESS_ITEM_PATTERN)].map(([item]) => item.trim()).filter(Boolean);\n}\n\n/**\n * Read one mailbox; a comment names a bare address, and a bare name has no address.\n *\n * @param item - One item of an address list.\n * @returns {PyPIContact} The name and email found in it.\n */\nfunction parseMailbox(item: string): PyPIContact {\n  const quoted = item.match(QUOTED_NAME_PATTERN);\n  if (quoted) {\n    return { name: quoted[1]!.replaceAll(/\\\\(.)/g, \"$1\").trim(), email: quoted[2]!.trim() };\n  }\n\n  const comments: string[] = [];\n  const bare = item\n    .replaceAll(COMMENT_PATTERN, (_match: string, comment: string) => {\n      comments.push(comment.trim());\n      return \" \";\n    })\n    .trim();\n  const comment = comments.filter(Boolean).join(\" \");\n  const angled = bare.match(ANGLE_ADDRESS_PATTERN);\n  if (angled) return { name: angled[1]!.trim() || comment, email: angled[2]!.trim() };\n  return bare.includes(\"@\") ? { name: comment, email: bare } : { name: bare || comment, email: \"\" };\n}\n\n/**\n * PEP 621 keeps people without an email in the name field, so the label stays its own entry.\n *\n * @param name - The `author` or `maintainer` field.\n * @param emails - The matching `author_email` or `maintainer_email` field.\n * @returns {PyPIContact[]} One contact per person.\n */\nfunction pypiContacts(\n  name: string | null | undefined,\n  emails: string | null | undefined,\n): PyPIContact[] {\n  const label = name?.trim() ?? \"\";\n  const contacts = splitAddressList(emails ?? \"\").map(parseMailbox);\n\n  if (!label) return contacts;\n  if (contacts.length === 1 && !contacts[0]!.name) {\n    return [{ name: label, email: contacts[0]!.email }];\n  }\n  if (contacts.some((contact) => contact.name.toLowerCase() === label.toLowerCase())) {\n    return contacts;\n  }\n  return [{ name: label, email: \"\" }, ...contacts];\n}\n\n/** PyPI registry client. */\nexport class PyPIRegistry extends Registry {\n  constructor(baseURL: string, client: Client) {\n    super();\n    this.baseURL = baseURL;\n    this.client = client;\n  }\n\n  readonly baseURL: string;\n  readonly client: Client;\n\n  private readonly downloadUrls = new Map<string, string>();\n\n  ecosystem(): string {\n    return \"pypi\";\n  }\n\n  async fetchPackage(name: string, signal?: AbortSignal): Promise<Package> {\n    const normalized = this.normalizeName(name);\n    const url = `${this.baseURL}/pypi/${normalized}/json`;\n\n    try {\n      const data = await this.client.getJSON<PyPIPackageResponse>(url, signal);\n      const info = data.info;\n\n      const licenses = pypiLicense(info);\n      const repository = this.extractRepository(info.project_urls);\n      const keywords = this.parseKeywords(info.keywords);\n\n      return {\n        name: info.name,\n        description: info.summary || info.description || \"\",\n        homepage: this.findProjectUrl(info.project_urls, [\"Homepage\"]),\n        documentation: this.findProjectUrl(info.project_urls, [\"Documentation\"]),\n        repository,\n        licenses,\n        keywords,\n        namespace: \"\",\n        latestVersion: info.version,\n        metadata: {},\n      };\n    } catch (error) {\n      rethrowFetchError(error, this.ecosystem(), name);\n    }\n  }\n\n  async fetchVersions(name: string, signal?: AbortSignal): Promise<Version[]> {\n    const normalized = this.normalizeName(name);\n    const url = `${this.baseURL}/simple/${normalized}/`;\n\n    try {\n      const data = await this.client.getJSON<PyPISimpleResponse>(url, signal, {\n        Accept: \"application/vnd.pypi.simple.v1+json\",\n      });\n\n      const filesByVersion = this.indexFilesByVersion(normalized, data.versions, data.files);\n      return [...filesByVersion].map(([version, files]) =>\n        this.toVersion(normalized, version, files),\n      );\n    } catch (error) {\n      rethrowFetchError(error, this.ecosystem(), name);\n    }\n  }\n\n  async fetchDependencies(\n    name: string,\n    version: string,\n    signal?: AbortSignal,\n  ): Promise<Dependency[]> {\n    const normalized = this.normalizeName(name);\n    const url = `${this.baseURL}/pypi/${normalized}/${version}/json`;\n\n    try {\n      const data = await this.client.getJSON<PyPIPackageResponse>(url, signal);\n      const dependencies: Dependency[] = [];\n\n      if (data.info.requires_dist) {\n        for (const depStr of data.info.requires_dist) {\n          const dep = this.parsePEP508(depStr);\n          if (dep) {\n            dependencies.push(dep);\n          }\n        }\n      }\n\n      return dependencies;\n    } catch (error) {\n      rethrowFetchError(error, this.ecosystem(), name, version);\n    }\n  }\n\n  async fetchMaintainers(name: string, signal?: AbortSignal): Promise<Maintainer[]> {\n    const normalized = this.normalizeName(name);\n    const url = `${this.baseURL}/pypi/${normalized}/json`;\n\n    try {\n      const { info } = await this.client.getJSON<PyPIPackageResponse>(url, signal);\n      const groups = [\n        [\"author\", pypiContacts(info.author, info.author_email)],\n        [\"maintainer\", pypiContacts(info.maintainer, info.maintainer_email)],\n      ] as const;\n      const seen = new Set<string>();\n      const maintainers: Maintainer[] = [];\n\n      for (const [role, contacts] of groups) {\n        for (const contact of contacts) {\n          const key = (contact.email || contact.name).toLowerCase();\n          if (seen.has(key)) continue;\n          seen.add(key);\n          maintainers.push({\n            uuid: \"\",\n            login: contact.email ? contact.email.split(\"@\")[0]! : \"\",\n            name: contact.name,\n            email: contact.email,\n            url: \"\",\n            role,\n          });\n        }\n      }\n\n      return maintainers;\n    } catch (error) {\n      rethrowFetchError(error, this.ecosystem(), name);\n    }\n  }\n\n  urls(): URLBuilder {\n    return {\n      registry: (name: string, version?: string) => {\n        const normalized = this.normalizeName(name);\n        const base = `https://pypi.org/project/${normalized}`;\n        return version ? `${base}/${version}` : base;\n      },\n      download: (name: string, version: string) => {\n        const normalized = this.normalizeName(name);\n        return (\n          this.downloadUrls.get(`${normalized}@${version}`) ??\n          `https://pypi.org/project/${normalized}/${version}/`\n        );\n      },\n      documentation: (name: string, _version?: string) => {\n        const normalized = this.normalizeName(name);\n        return `https://pypi.org/project/${normalized}`;\n      },\n      readme: (name: string, version?: string) => {\n        const normalized = this.normalizeName(name);\n        return version\n          ? `https://pypi.org/project/${normalized}/${version}/`\n          : `https://pypi.org/project/${normalized}/`;\n      },\n      purl: (name: string, version?: string) => {\n        return buildPURL({ type: \"pypi\", name: this.normalizeName(name), version });\n      },\n    };\n  }\n\n  private indexFilesByVersion(\n    normalizedName: string,\n    versions: readonly string[],\n    files: readonly PyPISimpleFile[],\n  ): Map<string, PyPISimpleFile[]> {\n    const filesByVersion = new Map<string, PyPISimpleFile[]>();\n\n    for (const version of versions) filesByVersion.set(version, []);\n    for (const file of files) {\n      const version = this.matchFileVersion(file.filename, normalizedName, versions);\n      if (version) filesByVersion.get(version)!.push(file);\n    }\n\n    return filesByVersion;\n  }\n\n  private toVersion(\n    normalizedName: string,\n    version: string,\n    files: readonly PyPISimpleFile[],\n  ): Version {\n    if (files.length === 0) {\n      return {\n        number: version,\n        publishedAt: null,\n        licenses: \"\",\n        integrity: \"\",\n        status: \"\",\n        metadata: {},\n      };\n    }\n\n    const sdist = files.find((file) => file.filename.endsWith(\".tar.gz\"));\n    const file = sdist ?? files[0]!;\n    const publishedAt = file[\"upload-time\"] ? new Date(file[\"upload-time\"]) : null;\n    const integrity = file.hashes?.sha256 ? `sha256-${file.hashes.sha256}` : \"\";\n    const status = file.yanked ? \"yanked\" : \"\";\n\n    this.downloadUrls.set(`${normalizedName}@${version}`, file.url);\n\n    return {\n      number: version,\n      publishedAt,\n      licenses: \"\",\n      integrity,\n      status,\n      metadata: {},\n    };\n  }\n\n  private matchFileVersion(\n    filename: string,\n    normalizedName: string,\n    versions: readonly string[],\n  ): string | undefined {\n    const lower = filename.toLowerCase();\n    let prefix: string | undefined;\n\n    for (const separator of PYPI_FILENAME_SEPARATORS) {\n      const candidate = normalizedName.replaceAll(\"-\", separator) + \"-\";\n      if (lower.startsWith(candidate)) {\n        prefix = candidate;\n        break;\n      }\n    }\n\n    if (!prefix) return undefined;\n\n    const afterPrefix = filename.slice(prefix.length);\n\n    // Wheel: {name}-{version}-{python}-{abi}-{platform}.whl\n    // Version is before the first dash after the prefix\n    const dashIdx = afterPrefix.indexOf(\"-\");\n    if (dashIdx !== -1) {\n      const candidate = afterPrefix.slice(0, dashIdx);\n      if (versions.includes(candidate)) return candidate;\n    }\n\n    // Sdist: {name}-{version}.tar.gz / .zip / .tar.bz2\n    const stripped = afterPrefix.replace(/\\.(tar\\.(gz|bz2|xz)|zip)$/i, \"\");\n    if (versions.includes(stripped)) return stripped;\n\n    return undefined;\n  }\n\n  private normalizeName(name: string): string {\n    return name.toLowerCase().replaceAll(/[-_.]+/g, \"-\");\n  }\n\n  private extractRepository(projectUrls: Readonly<Record<string, string>> | undefined): string {\n    const url = this.findProjectUrl(projectUrls, [\n      \"Repository\",\n      \"Source\",\n      \"Source Code\",\n      \"GitHub\",\n      \"Homepage\",\n    ]);\n    return normalizeRepositoryURL(url);\n  }\n\n  private findProjectUrl(\n    projectUrls: Readonly<Record<string, string>> | undefined,\n    keys: readonly string[],\n  ): string {\n    if (!projectUrls) return \"\";\n\n    const lowered = new Map<string, string>();\n    for (const [k, v] of Object.entries(projectUrls)) {\n      lowered.set(k.toLowerCase(), v);\n    }\n\n    for (const key of keys) {\n      const value = lowered.get(key.toLowerCase());\n      if (value) return value;\n    }\n\n    return \"\";\n  }\n\n  private parseKeywords(keywords: string | undefined): string[] {\n    if (!keywords) return [];\n    return keywords\n      .split(\",\")\n      .map((k) => k.trim())\n      .filter(Boolean);\n  }\n\n  private parseExtraMarker(marker: string): Pick<Dependency, \"optional\" | \"scope\"> {\n    const extraMatch = marker.match(/extra\\s*==\\s*[\"']([^\"']+)[\"']/);\n    if (!extraMatch) return { scope: \"runtime\", optional: false };\n\n    const extraName = extraMatch[1]!.toLowerCase();\n    if (/^dev(elop(ment)?)?$/.test(extraName)) {\n      return { scope: \"development\", optional: true };\n    }\n    if (/^test(s|ing)?$/.test(extraName)) {\n      return { scope: \"test\", optional: true };\n    }\n    return { scope: \"runtime\", optional: true };\n  }\n\n  private parsePEP508(depStr: string): Dependency | null {\n    // PEP 508 format: name [extras] (version_spec) ; markers\n    const semiIdx = depStr.indexOf(\";\");\n    const mainPart = semiIdx === -1 ? depStr.trim() : depStr.slice(0, semiIdx).trim();\n    const markerStr = semiIdx === -1 ? \"\" : depStr.slice(semiIdx + 1).trim();\n\n    // Extract name, skip [extras] bracket group, capture version spec\n    const match = mainPart.match(/^([a-zA-Z0-9._-]+)\\s*(?:\\[.*?\\])?\\s*(.*)$/);\n    if (!match) return null;\n\n    const depName = match[1]!;\n    let versionSpec = match[2]!.trim();\n\n    // Strip surrounding parentheses: \"(<4,>=2)\" -> \"<4,>=2\"\n    if (versionSpec.startsWith(\"(\") && versionSpec.endsWith(\")\")) {\n      versionSpec = versionSpec.slice(1, -1).trim();\n    }\n\n    const marker = this.parseExtraMarker(markerStr);\n\n    return {\n      name: this.normalizeName(depName),\n      requirements: versionSpec,\n      scope: marker.scope,\n      optional: marker.optional,\n    };\n  }\n}\n"],"mappings":"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"}