{"version":3,"file":"npm.mjs","names":[],"sources":["../../src/registries/npm.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\ninterface NpmPerson {\n  readonly name?: string;\n  readonly email?: string;\n  readonly url?: string;\n}\n\n/** npm registry API response for a single package. */\ninterface NpmPackageResponse {\n  name: string;\n  description?: string;\n  homepage?: string;\n  repository?:\n    | {\n        type?: string;\n        url?: string;\n      }\n    | string;\n  license?:\n    | string\n    | {\n        type?: string;\n      };\n  keywords?: string[];\n  \"dist-tags\": {\n    latest: string;\n  };\n  versions: Record<string, NpmVersion>;\n  maintainers?: readonly NpmPerson[];\n  time?: Record<string, string>;\n}\n\n/** npm version data. */\ninterface NpmVersion {\n  name: string;\n  version: string;\n  description?: string;\n  license?:\n    | string\n    | {\n        type?: string;\n      };\n  keywords?: string[];\n  author?: NpmPerson;\n  contributors?: readonly NpmPerson[];\n  maintainers?: readonly NpmPerson[];\n  dependencies?: Record<string, string>;\n  devDependencies?: Record<string, string>;\n  optionalDependencies?: Record<string, string>;\n  peerDependencies?: Record<string, string>;\n  dist?: {\n    integrity?: string;\n    shasum?: string;\n    tarball?: string;\n  };\n  deprecated?: boolean | string;\n}\n\n/** npm registry client. */\nexport class NpmRegistry 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  ecosystem(): string {\n    return \"npm\";\n  }\n\n  async fetchPackage(name: string, signal?: AbortSignal): Promise<Package> {\n    const encodedName = this.encodeName(name);\n    const url = `${this.baseURL}/${encodedName}`;\n\n    try {\n      const data = await this.client.getJSON<NpmPackageResponse>(url, signal);\n\n      const latestVersion = data[\"dist-tags\"].latest;\n      const latestVersionData = data.versions[latestVersion];\n\n      const licenses = data.license\n        ? this.extractLicense(data.license)\n        : this.extractLicense(latestVersionData?.license);\n      const namespace = this.extractNamespace(name);\n\n      return {\n        name: data.name,\n        description: data.description || \"\",\n        homepage: data.homepage || \"\",\n        documentation: \"\",\n        repository: normalizeRepositoryURL(data.repository || \"\"),\n        licenses,\n        keywords: data.keywords ?? [],\n        namespace,\n        latestVersion,\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 encodedName = this.encodeName(name);\n    const url = `${this.baseURL}/${encodedName}`;\n\n    try {\n      const data = await this.client.getJSON<NpmPackageResponse>(url, signal);\n\n      return Object.entries(data.versions).map(([versionStr, versionData]) => {\n        const licenses = this.extractLicense(versionData.license);\n        const publishedAt = data.time?.[versionStr] ? new Date(data.time[versionStr]) : null;\n        const status = versionData.deprecated ? \"deprecated\" : \"\";\n        const integrity = versionData.dist?.integrity\n          ? versionData.dist.integrity\n          : versionData.dist?.shasum\n            ? `sha1-${versionData.dist.shasum}`\n            : \"\";\n\n        return {\n          number: versionStr,\n          publishedAt,\n          licenses,\n          integrity,\n          status,\n          metadata: {},\n        };\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 encodedName = this.encodeName(name);\n    const url = `${this.baseURL}/${encodedName}/${version}`;\n\n    try {\n      const versionData = await this.client.getJSON<NpmVersion>(url, signal);\n      const optionalNames = Object.keys(versionData.optionalDependencies ?? {});\n\n      return [\n        ...this.mapDependencies(versionData.dependencies, \"runtime\", false, optionalNames),\n        ...this.mapDependencies(versionData.devDependencies, \"development\", false),\n        ...this.mapDependencies(versionData.optionalDependencies, \"runtime\", true),\n        ...this.mapDependencies(versionData.peerDependencies, \"runtime\", false),\n      ];\n    } catch (error) {\n      rethrowFetchError(error, this.ecosystem(), name, version);\n    }\n  }\n\n  async fetchMaintainers(name: string, signal?: AbortSignal): Promise<Maintainer[]> {\n    const encodedName = this.encodeName(name);\n    const url = `${this.baseURL}/${encodedName}`;\n\n    try {\n      const data = await this.client.getJSON<NpmPackageResponse>(url, signal);\n      const latestVersion = data.versions[data[\"dist-tags\"].latest];\n\n      return this.collectMaintainers(\n        data.maintainers,\n        latestVersion?.author,\n        latestVersion?.contributors,\n      );\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 base = `https://www.npmjs.com/package/${name}`;\n        return version ? `${base}/v/${version}` : base;\n      },\n      download: (name: string, version: string) => {\n        const encodedName = this.encodeName(name);\n        const tarballName = name.includes(\"/\") ? name.split(\"/\")[1] : name;\n        return `https://registry.npmjs.org/${encodedName}/-/${tarballName}-${version}.tgz`;\n      },\n      documentation: (name: string, _version?: string) => {\n        return `https://www.npmjs.com/package/${name}`;\n      },\n      readme: (name: string, version?: string) => {\n        const ver = version ? `@${version}` : \"\";\n        return `https://cdn.jsdelivr.net/npm/${name}${ver}/README.md`;\n      },\n      purl: (name: string, version?: string) => {\n        const namespace = this.extractNamespace(name);\n        const bareName = namespace ? name.slice(namespace.length + 1) : name;\n        return buildPURL({ type: \"npm\", namespace, name: bareName, version });\n      },\n    };\n  }\n\n  private mapDependencies(\n    entries: Readonly<Record<string, string>> | undefined,\n    scope: Dependency[\"scope\"],\n    optional: boolean,\n    excluded: readonly string[] = [],\n  ): Dependency[] {\n    const dependencies: Dependency[] = [];\n\n    for (const [name, requirements] of Object.entries(entries ?? {})) {\n      if (excluded.includes(name)) continue;\n      dependencies.push({ name, requirements, scope, optional });\n    }\n\n    return dependencies;\n  }\n\n  private collectMaintainers(\n    maintainers: readonly NpmPerson[] | undefined,\n    author: Readonly<NpmPerson> | undefined,\n    contributors: readonly NpmPerson[] | undefined,\n  ): Maintainer[] {\n    const candidates: Maintainer[] = [\n      ...(maintainers ?? []).map((maintainer) => this.toAccount(maintainer)),\n      ...(author ? [this.toPerson(author, \"author\")] : []),\n      ...(contributors ?? []).map((contributor) => this.toPerson(contributor, \"contributor\")),\n    ];\n    const seen = new Set<string>();\n    const result: Maintainer[] = [];\n\n    for (const candidate of candidates) {\n      const key = candidate.email || candidate.name;\n      if (!key || seen.has(key)) continue;\n      seen.add(key);\n      result.push(candidate);\n    }\n\n    return result;\n  }\n\n  /**\n   * A `maintainers` entry is an npm account, and its `name` is the username.\n   *\n   * @param maintainer - One entry of the package document's `maintainers`.\n   * @returns {Maintainer} The account, with the username as both `login` and `name`.\n   */\n  private toAccount(maintainer: Readonly<NpmPerson>): Maintainer {\n    const login = maintainer.name || \"\";\n    return {\n      uuid: \"\",\n      login,\n      name: login,\n      email: maintainer.email || \"\",\n      url: maintainer.url || \"\",\n      role: \"\",\n    };\n  }\n\n  /**\n   * `author` and `contributors` come from package.json, where nobody has an npm login.\n   *\n   * @param person - One person of the latest version's `author` or `contributors`.\n   * @param role - The role that field stands for.\n   * @returns {Maintainer} The person, with an empty `login`.\n   */\n  private toPerson(person: Readonly<NpmPerson>, role: string): Maintainer {\n    return {\n      uuid: \"\",\n      login: \"\",\n      name: person.name || \"\",\n      email: person.email || \"\",\n      url: person.url || \"\",\n      role,\n    };\n  }\n\n  private encodeName(name: string): string {\n    if (name.startsWith(\"@\")) {\n      return name.replace(\"/\", \"%2F\");\n    }\n    return name;\n  }\n\n  private extractNamespace(name: string): string {\n    if (name.startsWith(\"@\")) {\n      const parts = name.split(\"/\");\n      return parts[0] || \"\";\n    }\n    return \"\";\n  }\n\n  private extractLicense(raw: string | Readonly<{ type?: string }> | undefined): string {\n    if (!raw) return \"\";\n\n    if (typeof raw === \"string\") {\n      return normalizeLicense(raw);\n    }\n\n    if (typeof raw === \"object\" && raw !== null) {\n      const obj = raw as Record<string, unknown>;\n      if (typeof obj[\"type\"] === \"string\") {\n        return normalizeLicense(obj[\"type\"]);\n      }\n    }\n\n    return \"\";\n  }\n}\n"],"mappings":"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"}