{"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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"}