"use strict";
/* Socket Lib - Built with rolldown */
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const require_primordials_error = require('../primordials/error.js');
const require_primordials_string = require('../primordials/string.js');
const require_primordials_array = require('../primordials/array.js');
const require_primordials_date = require('../primordials/date.js');
const require_primordials_json = require('../primordials/json.js');
const require_promises_retry = require('../promises/retry.js');
const require_http_request_request = require('../http-request/request.js');
const require_releases_github_assets = require('./github-assets.js');
const require_releases_github_auth = require('./github-auth.js');
const require_releases_github_retry_config = require('./github-retry-config.js');

//#region src/releases/github-listing.ts
/**
* @file GitHub release listing via REST + GraphQL. Split out of
*   `releases/github-api.ts` for size hygiene. Holds the "list all releases for
*   a repo" path (both transports + the latest-matching-tag picker that
*   composes them):
*
*   - `fetchReleasesViaRest` — canonical REST `/releases?per_page=100` listing
*   - `fetchReleasesViaGraphQL` — GraphQL fallback when REST's ES index is
*     degraded
*   - `getLatestRelease` — REST → GraphQL fallback + prefix/asset filter The
*     per-tag asset-URL lookup lives in `./github-asset-url`.
*/
/**
* Fetch the latest 100 releases for a repo via GraphQL.
*
* Why this exists: `fetchReleasesViaRest` can return `[]` for two reasons (real
* empty repo vs. GitHub-incident-degraded backend). When REST returns nothing,
* the caller in `getLatestRelease` calls THIS to disambiguate — if we return >0
* here, REST was lying.
*
* Field shape diffs we normalize: GraphQL returns REST equivalent Why they
* differ `tagName` `tag_name` camelCase vs. snake_case `publishedAt`
* `published_at` camelCase vs. snake_case `releaseAssets.nodes` `assets`
* GraphQL connection wrapper unwrapped.
*
* We re-shape inside the `.map(...)` at the bottom so callers downstream can
* use the SAME code path regardless of which transport ran.
*
* Why we hit a different backend: GraphQL queries don't go through the same
* Elasticsearch index that REST listings rely on. During incidents that drop
* the ES index (or its connectivity), GraphQL's `repository.releases`
* connection keeps working because it reads from a different data path inside
* GitHub. That's the entire reason this fallback exists.
*/
async function fetchReleasesViaGraphQL(owner, repo) {
	const response = await require_http_request_request.httpRequest("https://api.github.com/graphql", {
		body: require_primordials_json.JSONStringify({
			query: `query($owner: String!, $repo: String!) {
        repository(owner: $owner, name: $repo) {
          releases(first: 100, orderBy: {field: CREATED_AT, direction: DESC}) {
            nodes {
              tagName
              publishedAt
              releaseAssets(first: 100) { nodes { name } }
            }
          }
        }
      }`,
			variables: {
				owner,
				repo
			}
		}),
		headers: {
			...require_releases_github_auth.getAuthHeaders(),
			"Content-Type": "application/json"
		},
		method: "POST"
	});
	if (!response.ok) throw new require_primordials_error.ErrorCtor(`Failed to fetch ${owner}/${repo} releases (GraphQL): ${response.status}`);
	let parsed;
	try {
		parsed = require_primordials_json.JSONParse(response.body.toString("utf8"));
	} catch (cause) {
		throw new require_primordials_error.ErrorCtor(`Failed to parse GitHub GraphQL response for ${owner}/${repo} releases`, { cause });
	}
	/* c8 ignore start */
	if (parsed.errors?.length) throw new require_primordials_error.ErrorCtor(`GraphQL repository.releases(${owner}/${repo}) returned errors: ${parsed.errors.map((e) => e.message).join("; ")}`);
	return (parsed.data?.repository?.releases?.nodes ?? []).map((n) => ({
		tag_name: n.tagName,
		published_at: n.publishedAt,
		assets: n.releaseAssets?.nodes ?? []
	}));
	/* c8 ignore stop */
}
/**
* Fetch the latest 100 releases for a repo via REST.
*
* Why this returns `[]` on TWO different cases:
*
* - HTTP 200 + zero-byte body. This is the documented GitHub "search degraded"
*   incident shape (see status.github.com). The releases listing endpoint
*   shares an Elasticsearch index with search; when that ES is degraded,
*   `/releases` returns a successful 200 OK but with NO BODY. There's no error
*   code, no Retry-After, no rate-limit header — just an empty payload.
* - HTTP 200 + literal `[]`. This is the _normal_ "the repo has no releases"
*   response — say a brand-new repo with no published versions.
*
* Both produce the same `[]` here because the helper can't tell them apart
* without context. The CALLER (getLatestRelease) does the cross-check: if REST
* returns `[]`, query GraphQL once. If GraphQL also returns `[]`, the repo
* really is empty. If it returns >0, REST was lying and we use GraphQL's
* answer.
*
* Why we throw on non-OK status: `pRetry` wraps this call and retries on thrown
* errors with exponential backoff. A 5xx is transient and worth retrying; we
* want it to throw so pRetry can do its job. Empty body is NOT thrown because
* pRetry can't help — a 200 OK is "done" as far as retry policy is concerned.
*/
async function fetchReleasesViaRest(owner, repo) {
	const response = await require_http_request_request.httpRequest(`https://api.github.com/repos/${owner}/${repo}/releases?per_page=100`, { headers: require_releases_github_auth.getAuthHeaders() });
	if (!response.ok) throw new require_primordials_error.ErrorCtor(`Failed to fetch ${owner}/${repo} releases: ${response.status}`);
	const text = response.body.toString("utf8");
	if (text.length === 0) return [];
	let parsed;
	try {
		parsed = require_primordials_json.JSONParse(text);
	} catch (cause) {
		throw new require_primordials_error.ErrorCtor(`Failed to parse ${owner}/${repo} releases response`, { cause });
	}
	/* c8 ignore start */
	return require_primordials_array.ArrayIsArray(parsed) ? parsed : [];
	/* c8 ignore stop */
}
/**
* Get latest release tag matching a tool prefix. Optionally filter by releases
* containing a matching asset.
*
* @example
*   ;```typescript
*   const tag = await getLatestRelease('lief-', {
*     owner: 'SocketDev',
*     repo: 'socket-btm',
*   })
*   console.log(tag) // 'lief-2025-01-15-abc1234'
*   ```
*
* @param toolPrefix - Tool name prefix to search for (e.g., 'node-smol-')
* @param repoConfig - Repository configuration (owner/repo)
* @param options - Additional options.
* @param options.assetPattern - Optional pattern to filter releases by matching
*   asset.
* @param options.nothrow - If true, return undefined instead of throwing when
*   both REST and GraphQL backends are degraded. Default: false.
*
* @returns Latest release tag or undefined if not found
*
* @throws {Error} If both REST and GraphQL backends are degraded and nothrow is
*   false.
*/
async function getLatestRelease(toolPrefix, repoConfig, options = {}) {
	const { assetPattern, nothrow = false } = options;
	const { owner, repo } = repoConfig;
	const isMatch = assetPattern ? require_releases_github_assets.createAssetMatcher(assetPattern) : void 0;
	return await /* @__PURE__ */ require_promises_retry.pRetry(async () => {
		let releases = await fetchReleasesViaRest(owner, repo);
		if (releases.length === 0) {
			let graphqlReleases;
			try {
				graphqlReleases = await fetchReleasesViaGraphQL(owner, repo);
			} catch (cause) {
				/* c8 ignore next 7 - REST + GraphQL both-degraded branch
				requires both real backends to fail simultaneously. */
				if (nothrow) return;
				throw new require_primordials_error.ErrorCtor(`Failed to list ${owner}/${repo} releases: both REST and GraphQL backends degraded`, { cause });
			}
			if (graphqlReleases.length > 0) releases = graphqlReleases;
		}
		const matchingReleases = releases.filter((release) => {
			const { assets, tag_name: tag } = release;
			if (!require_primordials_string.StringPrototypeStartsWith(tag, toolPrefix)) return false;
			if (!assets || assets.length === 0) return false;
			if (isMatch) {
				if (!assets.some((a) => isMatch(a.name))) return false;
			}
			return true;
		});
		if (matchingReleases.length === 0) return;
		matchingReleases.sort((a, b) => require_primordials_date.DateParse(b.published_at) - require_primordials_date.DateParse(a.published_at));
		return matchingReleases[0].tag_name;
	}, require_releases_github_retry_config.GITHUB_RETRY_CONFIG) ?? void 0;
}

//#endregion
exports.fetchReleasesViaGraphQL = fetchReleasesViaGraphQL;
exports.fetchReleasesViaRest = fetchReleasesViaRest;
exports.getLatestRelease = getLatestRelease;