import { readFile } from "node:fs/promises"; const result = (state, description) => ({ state, description }); const OPEN_HEADS_QUERY = `query ReadinessOpenHeads($owner: String!, $name: String!, $after: String) { repository(owner: $owner, name: $name) { pullRequests(first: 100, after: $after, states: OPEN, orderBy: {field: CREATED_AT, direction: ASC}) { nodes { number headRefOid } pageInfo { hasNextPage endCursor } } } }`; /** An unindexed commit needs complete live head coverage, not PR bodies. */ async function openHeadsMatching(github, repository, sha) { const matches = []; const numbers = new Set(); const cursors = new Set(); let after = null; do { const response = await github.graphql(OPEN_HEADS_QUERY, { owner: repository.owner, name: repository.repo, after, }); const page = response?.repository?.pullRequests; if ( !Array.isArray(page?.nodes) || typeof page.pageInfo?.hasNextPage !== "boolean" ) throw new Error("GitHub returned incomplete open-PR head metadata."); for (const node of page.nodes) { if ( !Number.isSafeInteger(node?.number) || node.number <= 0 || typeof node.headRefOid !== "string" || !/^(?:[a-f\d]{40}|[a-f\d]{64})$/u.test(node.headRefOid) || numbers.has(node.number) ) throw new Error( "GitHub returned invalid or repeated open-PR head metadata.", ); numbers.add(node.number); if (node.headRefOid === sha) matches.push({ number: node.number, state: "open", head: { sha } }); } if (!page.pageInfo.hasNextPage) return matches; const next = page.pageInfo.endCursor; if (typeof next !== "string" || next.length === 0 || cursors.has(next)) throw new Error("GitHub open-PR head pagination did not advance."); cursors.add(next); after = next; } while (true); } function readinessLines(markdown) { const lines = markdown.split(/\r?\n/u); const visible = []; let fence; let comment = false; for (const source of lines) { if (fence) { if ( new RegExp( `^ {0,3}${fence.character}{${fence.length},}\\s*$`, "u", ).test(source) ) fence = undefined; continue; } const opening = !comment && /^ {0,3}(`{3,}|~{3,})/u.exec(source); if (opening) { fence = { character: opening[1][0], length: opening[1].length }; continue; } let line = ""; let offset = 0; while (offset < source.length) { const marker = comment ? "-->" : "