{"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":";;;;AAkEA,IAAa,cAAb,cAAiC,SAAS;CACxC,YAAY,SAAiB,QAAgB;EAC3C,MAAM;EACN,KAAK,UAAU;EACf,KAAK,SAAS;CAChB;CAEA;CACA;CAEA,YAAoB;EAClB,OAAO;CACT;CAEA,MAAM,aAAa,MAAc,QAAwC;EACvE,MAAM,cAAc,KAAK,WAAW,IAAI;EACxC,MAAM,MAAM,GAAG,KAAK,QAAQ,GAAG;EAE/B,IAAI;GACF,MAAM,OAAO,MAAM,KAAK,OAAO,QAA4B,KAAK,MAAM;GAEtE,MAAM,gBAAgB,KAAK,YAAY,CAAC;GACxC,MAAM,oBAAoB,KAAK,SAAS;GAExC,MAAM,WAAW,KAAK,UAClB,KAAK,eAAe,KAAK,OAAO,IAChC,KAAK,eAAe,mBAAmB,OAAO;GAClD,MAAM,YAAY,KAAK,iBAAiB,IAAI;GAE5C,OAAO;IACL,MAAM,KAAK;IACX,aAAa,KAAK,eAAe;IACjC,UAAU,KAAK,YAAY;IAC3B,eAAe;IACf,YAAY,uBAAuB,KAAK,cAAc,EAAE;IACxD;IACA,UAAU,KAAK,YAAY,CAAC;IAC5B;IACA;IACA,UAAU,CAAC;GACb;EACF,SAAS,OAAO;GACd,kBAAkB,OAAO,KAAK,UAAU,GAAG,IAAI;EACjD;CACF;CAEA,MAAM,cAAc,MAAc,QAA0C;EAC1E,MAAM,cAAc,KAAK,WAAW,IAAI;EACxC,MAAM,MAAM,GAAG,KAAK,QAAQ,GAAG;EAE/B,IAAI;GACF,MAAM,OAAO,MAAM,KAAK,OAAO,QAA4B,KAAK,MAAM;GAEtE,OAAO,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,KAAK,CAAC,YAAY,iBAAiB;IACtE,MAAM,WAAW,KAAK,eAAe,YAAY,OAAO;IACxD,MAAM,cAAc,KAAK,OAAO,cAAc,IAAI,KAAK,KAAK,KAAK,WAAW,IAAI;IAChF,MAAM,SAAS,YAAY,aAAa,eAAe;IAOvD,OAAO;KACL,QAAQ;KACR;KACA;KACA,WAVgB,YAAY,MAAM,YAChC,YAAY,KAAK,YACjB,YAAY,MAAM,SAChB,QAAQ,YAAY,KAAK,WACzB;KAOJ;KACA,UAAU,CAAC;IACb;GACF,CAAC;EACH,SAAS,OAAO;GACd,kBAAkB,OAAO,KAAK,UAAU,GAAG,IAAI;EACjD;CACF;CAEA,MAAM,kBACJ,MACA,SACA,QACuB;EACvB,MAAM,cAAc,KAAK,WAAW,IAAI;EACxC,MAAM,MAAM,GAAG,KAAK,QAAQ,GAAG,YAAY,GAAG;EAE9C,IAAI;GACF,MAAM,cAAc,MAAM,KAAK,OAAO,QAAoB,KAAK,MAAM;GACrE,MAAM,gBAAgB,OAAO,KAAK,YAAY,wBAAwB,CAAC,CAAC;GAExE,OAAO;IACL,GAAG,KAAK,gBAAgB,YAAY,cAAc,WAAW,OAAO,aAAa;IACjF,GAAG,KAAK,gBAAgB,YAAY,iBAAiB,eAAe,KAAK;IACzE,GAAG,KAAK,gBAAgB,YAAY,sBAAsB,WAAW,IAAI;IACzE,GAAG,KAAK,gBAAgB,YAAY,kBAAkB,WAAW,KAAK;GACxE;EACF,SAAS,OAAO;GACd,kBAAkB,OAAO,KAAK,UAAU,GAAG,MAAM,OAAO;EAC1D;CACF;CAEA,MAAM,iBAAiB,MAAc,QAA6C;EAChF,MAAM,cAAc,KAAK,WAAW,IAAI;EACxC,MAAM,MAAM,GAAG,KAAK,QAAQ,GAAG;EAE/B,IAAI;GACF,MAAM,OAAO,MAAM,KAAK,OAAO,QAA4B,KAAK,MAAM;GACtE,MAAM,gBAAgB,KAAK,SAAS,KAAK,YAAY,CAAC;GAEtD,OAAO,KAAK,mBACV,KAAK,aACL,eAAe,QACf,eAAe,YACjB;EACF,SAAS,OAAO;GACd,kBAAkB,OAAO,KAAK,UAAU,GAAG,IAAI;EACjD;CACF;CAEA,OAAmB;EACjB,OAAO;GACL,WAAW,MAAc,YAAqB;IAC5C,MAAM,OAAO,iCAAiC;IAC9C,OAAO,UAAU,GAAG,KAAK,KAAK,YAAY;GAC5C;GACA,WAAW,MAAc,YAAoB;IAG3C,OAAO,8BAFa,KAAK,WAAW,IAEW,EAAE,KAD7B,KAAK,SAAS,GAAG,IAAI,KAAK,MAAM,GAAG,CAAC,CAAC,KAAK,KACI,GAAG,QAAQ;GAC/E;GACA,gBAAgB,MAAc,aAAsB;IAClD,OAAO,iCAAiC;GAC1C;GACA,SAAS,MAAc,YAAqB;IAE1C,OAAO,gCAAgC,OAD3B,UAAU,IAAI,YAAY,GACY;GACpD;GACA,OAAO,MAAc,YAAqB;IACxC,MAAM,YAAY,KAAK,iBAAiB,IAAI;IAC5C,MAAM,WAAW,YAAY,KAAK,MAAM,UAAU,SAAS,CAAC,IAAI;IAChE,OAAO,UAAU;KAAE,MAAM;KAAO;KAAW,MAAM;KAAU;IAAQ,CAAC;GACtE;EACF;CACF;CAEA,gBACE,SACA,OACA,UACA,WAA8B,CAAC,GACjB;EACd,MAAM,eAA6B,CAAC;EAEpC,KAAK,MAAM,CAAC,MAAM,iBAAiB,OAAO,QAAQ,WAAW,CAAC,CAAC,GAAG;GAChE,IAAI,SAAS,SAAS,IAAI,GAAG;GAC7B,aAAa,KAAK;IAAE;IAAM;IAAc;IAAO;GAAS,CAAC;EAC3D;EAEA,OAAO;CACT;CAEA,mBACE,aACA,QACA,cACc;EACd,MAAM,aAA2B;GAC/B,IAAI,eAAe,CAAC,EAAA,CAAG,KAAK,eAAe,KAAK,UAAU,UAAU,CAAC;GACrE,GAAI,SAAS,CAAC,KAAK,SAAS,QAAQ,QAAQ,CAAC,IAAI,CAAC;GAClD,IAAI,gBAAgB,CAAC,EAAA,CAAG,KAAK,gBAAgB,KAAK,SAAS,aAAa,aAAa,CAAC;EACxF;EACA,MAAM,uBAAO,IAAI,IAAY;EAC7B,MAAM,SAAuB,CAAC;EAE9B,KAAK,MAAM,aAAa,YAAY;GAClC,MAAM,MAAM,UAAU,SAAS,UAAU;GACzC,IAAI,CAAC,OAAO,KAAK,IAAI,GAAG,GAAG;GAC3B,KAAK,IAAI,GAAG;GACZ,OAAO,KAAK,SAAS;EACvB;EAEA,OAAO;CACT;;;;;;GAQQ,MAAA;GACN,OAAM,WAAQ,SAAW;GACzB,KAAA,WAAO,OAAA;GACL,MAAM;EACN;CACA;CAEA,SAAK,QAAA,MAAW;EAChB,OAAM;GACR,MAAA;GACF,OAAA;;;;;;;;EASQ,IAAA,KAAS,WAA6B,GAA0B,GAAA,OAAA,KAAA,QAAA,KAAA,KAAA;EACtE,OAAO;CACL;CACA,iBAAO,MAAA;EACP,IAAA,KAAM,WAAO,GAAQ,GAAA,OAAA,KAAA,MAAA,GAAA,CAAA,CAAA,MAAA;EACrB,OAAA;CACA;CACA,eAAA,KAAA;EACF,IAAA,CAAA,KAAA,OAAA;EACF,IAAA,OAAA,QAAA,UAAA,OAAA,iBAAA,GAAA;EAEQ,IAAA,OAAW,QAAsB,YAAA,QAAA,MAAA;GACvC,MAAI,MAAK;GAGT,IAAA,OAAO,IAAA,YAAA,UAAA,OAAA,iBAAA,IAAA,OAAA;EACT;EAEQ,OAAA;CACN;AAIA;AAGF,SAAQ"}