{"version":3,"sources":["x:\\make\\devel\\vpm-listing-generator\\dist\\index.cjs","../src/index.ts"],"names":[],"mappings":"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","file":"x:\\make\\devel\\vpm-listing-generator\\dist\\index.cjs","sourcesContent":[null,"import fetchBuilder from \"fetch-retry\";\nimport type { Octokit } from \"octokit\";\nimport PQueue from \"p-queue\";\nimport type { GqlResponse } from \"./GqlTypes.js\";\nimport { type Listing, assertListing } from \"./Listing.js\";\nimport { type Package, assertPackage } from \"./Package.js\";\nimport { type Source, assertSource } from \"./Source.js\";\nimport { type StrictPackage, assertStrictPackage } from \"./StrictPackage.js\";\nconst fetch = fetchBuilder(globalThis.fetch);\n\n// The old Release type was based on the REST API.\n// We create a new compatible type for the processing loop.\ntype Release = {\n  name: string;\n  tag_name: string;\n  assets: {\n    name: string;\n    browser_download_url: string;\n  }[];\n};\n\nexport type RetryDelayFunction = (\n  attempt: number,\n  error: Error | null,\n  response: Response | null,\n) => number;\n\nexport type RetryDelayOption = number | RetryDelayFunction;\n\nexport type RetryOnFunction = (\n  attempt: number,\n  error: Error | null,\n  response: Response | null,\n) => boolean | Promise<boolean>;\n\nexport type RetryOnOption = number[] | RetryOnFunction;\n\n/** generate vpm repository listing json */\nexport async function generate(\n  /** source json */\n  source: Source,\n  /** options */\n  options: {\n    /** octokit to call Github API */\n    octokit: Octokit;\n    /** generate zipSHA256 */\n    calcSHA256?: boolean;\n    /** logger ex: console.log */\n    logger?: (message: string) => unknown;\n    /** fetch ZIP concurrency */\n    concurrency?: number;\n    /** @deprecated Not used anymore since switching to batched GraphQL queries. */\n    apiConcurrency?: number;\n    /** GraphQL API fetch repo batch size */\n    apiBatchRepositorySize?: number;\n    /** skip assert if false */\n    check?: boolean;\n    retries?: number;\n    retryDelay?: RetryDelayOption;\n    retryOn?: RetryOnOption;\n    /** additional kv on version entry */\n    additionalOnVersion?: (context: {\n      githubRepo: string;\n      package: Package;\n      release: Release;\n      addFetchQueue<T>(job: () => T): Promise<T>;\n    }) => Promise<Record<string, unknown>> | Record<string, unknown>;\n  },\n): Promise<Listing> {\n  const {\n    octokit,\n    logger,\n    calcSHA256 = true,\n    concurrency = 6,\n    apiBatchRepositorySize = 20,\n    additionalOnVersion,\n    check = true,\n    retries = 6,\n    retryDelay = (attempt) => 2 ** attempt * 1000,\n    retryOn = [400, 403, 408, 429, 500, 502, 503, 504, 618],\n  } = options;\n\n  async function fetchPackageJson(url: string) {\n    const res = await fetch(url, {\n      redirect: \"follow\",\n      retries,\n      retryDelay,\n      retryOn,\n    });\n    if (!res.ok) {\n      throw new Error(\n        `[${res.status}] Failed to fetch package.json from ${url}`,\n      );\n    }\n    const json = (await res.json()) as Package;\n    return check ? assertPackage(json) : json;\n  }\n\n  async function fetchZipSHA256(url: string) {\n    const res = await fetch(url, {\n      redirect: \"follow\",\n      retries,\n      retryDelay,\n      retryOn,\n    });\n    if (!res.ok) {\n      throw new Error(`[${res.status}] Failed to fetch zip file from ${url}`);\n    }\n    const buffer = await res.arrayBuffer();\n    const hashBuffer = await crypto.subtle.digest(\"SHA-256\", buffer);\n    return Array.from(new Uint8Array(hashBuffer))\n      .map((b) => b.toString(16).padStart(2, \"0\"))\n      .join(\"\");\n  }\n\n  if (check) assertSource(source);\n\n  const log = logger ?? (() => {});\n  const githubRepos = source.githubRepos ?? [];\n  const packages: Listing[\"packages\"] = {};\n\n  const fetchQueue = new PQueue({ concurrency });\n\n  const allReleases: {\n    [name: string]: Release[];\n  } = {};\n  for (const repo of githubRepos) {\n    allReleases[repo] = [];\n  }\n\n  for (let i = 0; i < githubRepos.length; i += apiBatchRepositorySize) {\n    const chunk = githubRepos.slice(i, i + apiBatchRepositorySize);\n    let reposToFetch: { name: string; cursor: string | null }[] = chunk.map(\n      (repo) => ({ name: repo, cursor: null }),\n    );\n\n    while (reposToFetch.length > 0) {\n      const queryFragments = reposToFetch.map(({ name, cursor }, i) => {\n        const [owner, repo] = name.split(\"/\");\n        const after = cursor ? `, after: \"${cursor}\"` : \"\";\n        return `\n        repo_${i}: repository(owner: \"${owner}\", name: \"${repo}\") {\n          releases(first: 100, orderBy: {field: CREATED_AT, direction: DESC}${after}) {\n            nodes {\n              name\n              tagName\n              releaseAssets(first: 100) {\n                nodes {\n                  name\n                  downloadUrl\n                }\n              }\n            }\n            pageInfo {\n              hasNextPage\n              endCursor\n            }\n          }\n        }`;\n      });\n\n      const query = `query {\\n${queryFragments.join(\"\\n\")}\\n}`;\n\n      log(`Fetching releases for ${reposToFetch.length} repositories.`);\n      const gqlResponse = (await octokit.graphql(query)) as GqlResponse;\n\n      const nextReposToFetch: { name: string; cursor: string | null }[] = [];\n      for (let i = 0; i < reposToFetch.length; i++) {\n        const repoName = reposToFetch[i].name;\n        const repoData = gqlResponse[`repo_${i}`];\n        if (!repoData) {\n          log(`[${repoName}] No data returned from GraphQL.`);\n          continue;\n        }\n\n        const releases = repoData.releases.nodes.map((release) => ({\n          name: release.name,\n          tag_name: release.tagName,\n          assets: release.releaseAssets.nodes.map((asset) => ({\n            name: asset.name,\n            browser_download_url: asset.downloadUrl,\n          })),\n        }));\n        allReleases[repoName].push(...releases);\n\n        if (repoData.releases.pageInfo.hasNextPage) {\n          nextReposToFetch.push({\n            name: repoName,\n            cursor: repoData.releases.pageInfo.endCursor,\n          });\n        }\n      }\n      reposToFetch = nextReposToFetch;\n    }\n  }\n\n  await Promise.all(\n    githubRepos.map(async (githubRepo) => {\n      const releases = allReleases[githubRepo];\n      const repoPackages: Listing[\"packages\"] = {};\n      await Promise.all(\n        releases.map(async (release) => {\n          const packageJson = release.assets.find(\n            (asset) => asset.name === \"package.json\",\n          );\n          if (!packageJson) {\n            log(\n              `[${githubRepo}](${release.name}) Skipping release because it does not contain package.json`,\n            );\n            return;\n          }\n\n          const pkg = await fetchQueue.add(() => {\n            log(\n              `[${githubRepo}](${release.name}) Fetching package.json from ${packageJson.browser_download_url}`,\n            );\n            return fetchPackageJson(packageJson.browser_download_url);\n          });\n          if (!pkg) throw new Error(\"Failed to fetch package.json\");\n          const zipName = `${pkg.name}-${pkg.version}.zip`;\n          const zip = release.assets.find((asset) => asset.name === zipName);\n          if (!zip) {\n            throw new Error(\n              `Failed to find zip file ${zipName} in release ${githubRepo} ${release.name}`,\n            );\n          }\n          let zipSHA256: undefined | string;\n          if (calcSHA256) {\n            const res = await fetchQueue.add(() => {\n              log(\n                `[${githubRepo}](${release.name}) \\\"${zipName}\\\" Fetching zip file from ${zip.browser_download_url}`,\n              );\n              return fetchZipSHA256(zip.browser_download_url);\n            });\n            if (res) zipSHA256 = res;\n          }\n          const additional = additionalOnVersion\n            ? await additionalOnVersion({\n                githubRepo,\n                package: pkg,\n                release,\n                addFetchQueue: fetchQueue.add,\n              })\n            : {};\n          const strictPackage: StrictPackage = zipSHA256\n            ? {\n                ...pkg,\n                url: zip.browser_download_url,\n                zipSHA256,\n                ...additional,\n              }\n            : {\n                ...pkg,\n                url: zip.browser_download_url,\n                ...additional,\n              };\n          if (!repoPackages[pkg.name])\n            repoPackages[pkg.name] = { versions: {} };\n          repoPackages[pkg.name].versions[pkg.version] = check\n            ? assertStrictPackage(strictPackage)\n            : strictPackage;\n        }),\n      );\n      for (const [id, data] of Object.entries(repoPackages)) {\n        if (packages[id]) {\n          packages[id].versions = {\n            ...packages[id].versions,\n            ...data.versions,\n          };\n        } else {\n          packages[id] = data;\n        }\n      }\n    }),\n  );\n  const listing: Listing = {\n    id: source.id,\n    name: source.name,\n    author: source.author.name,\n    url: source.url,\n    packages,\n  };\n  return check ? assertListing(listing) : listing;\n}\n"]}