UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

166 lines • 7.75 kB
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= import { githubPages } from "./paginate.js"; import { graphqlData } from "./repo.js"; function path(ref, tail = '') { return `/repos/${ref.owner}/${ref.repo}/pulls${tail}`; } export function listPulls(transport, ref, params, limit, include) { return githubPages(transport, path(ref), { params, limit, ...(include === undefined ? {} : { include }), }); } export function getPull(transport, ref, number) { return transport.get(path(ref, `/${String(number)}`)); } /** * The selected fields of one pull request, over GraphQL, as gh's * PullRequestByNumber asks for them for `pr view --json`: one query naming * only what was asked for. A selection that reads the page of a connection * after `$endCursor` is given that cursor as `endCursor`. * * Args: * transport (GitHubTransport): the API client. * ref (RepoRef): the repository. * number (number): the pull request. * selection (string): the GraphQL selection inside `pullRequest { }`. * endCursor (string | undefined): the cursor `$endCursor` carries. */ export async function pullRequestFields(transport, ref, number, selection, endCursor) { const variables = { owner: ref.owner, repo: ref.repo, pr_number: number }; if (endCursor !== undefined) variables.endCursor = endCursor; const cursor = endCursor === undefined ? '' : ', $endCursor: String'; const data = await graphqlData(transport, `query PullRequestByNumber($owner: String!, $repo: String!, $pr_number: Int!${cursor}) {\n` + ' repository(owner: $owner, name: $repo) {\n' + ` pullRequest(number: $pr_number) {${selection}}\n` + ' }\n }', variables); const repository = data.repository; const pull = repository?.pullRequest; return pull !== null && typeof pull === 'object' ? pull : {}; } const PULL_LIST = ' query PullRequestList(\n $owner: String!,\n $repo: String!,\n' + ' $limit: Int!,\n $endCursor: String,\n $baseBranch: String,\n' + ' $headBranch: String,\n $state: [PullRequestState!] = OPEN\n ) {\n' + ' repository(owner: $owner, name: $repo) {\n pullRequests(\n' + ' states: $state,\n baseRefName: $baseBranch,\n' + ' headRefName: $headBranch,\n first: $limit,\n' + ' after: $endCursor,\n orderBy: {field: CREATED_AT, direction: DESC}\n' + ' ) {\n totalCount\n nodes {\n ...pr\n' + ' }\n pageInfo {\n hasNextPage\n endCursor\n' + ' }\n }\n }\n }'; /** * The selected fields of a repository's pull requests, over GraphQL, as * gh's PullRequestList asks for them for `pr list --json`: newest first, a * page of up to 100 at a time until `limit`. A pull request a later page * repeats is listed once, which gh can only tell when the line asked for * `number`. * * Args: * transport (GitHubTransport): the API client. * ref (RepoRef): the repository. * filter (PullListFilter): the states and the base and head branches. * limit (number): how many pull requests at most. * selection (string): the GraphQL selection for each pull request. */ export async function listPullRequestFields(transport, ref, filter, limit, selection) { const query = `fragment pr on PullRequest{${selection}}\n${PULL_LIST}`; const rows = []; const seen = new Set(); let cursor = null; let pageLimit = Math.min(limit, 100); while (rows.length < limit) { const variables = { owner: ref.owner, repo: ref.repo, limit: pageLimit, state: filter.states, }; if (filter.base !== undefined && filter.base !== '') variables.baseBranch = filter.base; if (filter.head !== undefined && filter.head !== '') variables.headBranch = filter.head; if (cursor !== null) variables.endCursor = cursor; const data = await graphqlData(transport, query, variables); const page = data.repository?.pullRequests; for (const node of page?.nodes ?? []) { const number = node.number; if (typeof number === 'number' && number > 0) { if (seen.has(number)) continue; seen.add(number); } rows.push(node); if (rows.length === limit) break; } const next = page?.pageInfo?.endCursor ?? null; if (page?.pageInfo?.hasNextPage !== true || next === null || next === cursor) break; cursor = next; pageLimit = Math.min(pageLimit, limit - rows.length); } return rows; } export function createPull(transport, ref, body) { return transport.request('POST', path(ref), body); } export function editPull(transport, ref, number, body) { return transport.request('PATCH', path(ref, `/${String(number)}`), body); } export function mergePull(transport, ref, number, body) { return transport.request('PUT', path(ref, `/${String(number)}/merge`), Object.keys(body).length === 0 ? undefined : body); } export async function commentPull(transport, ref, number, body) { await getPull(transport, ref, number); return transport.request('POST', `/repos/${ref.owner}/${ref.repo}/issues/${String(number)}/comments`, { body }); } export async function diffPull(transport, ref, number) { const value = await transport.request('GET', path(ref, `/${String(number)}`), undefined, undefined, { Accept: 'application/vnd.github.v3.diff' }); return typeof value === 'string' ? value : ''; } const STATUS_CONCLUSIONS = ['error', 'failure', 'success']; function statusCheck(row) { const state = typeof row.state === 'string' ? row.state : ''; const done = STATUS_CONCLUSIONS.includes(state); return { name: row.context ?? '', status: done ? 'completed' : state, conclusion: done ? state : null, details_url: row.target_url ?? '', output: { summary: row.description ?? '' }, started_at: row.created_at ?? null, completed_at: row.updated_at ?? null, }; } export async function commitStatuses(transport, ref, sha) { const value = (await transport.get(`/repos/${ref.owner}/${ref.repo}/commits/${sha}/status`)); const rows = value?.statuses; if (!Array.isArray(rows)) return []; return rows.filter((row) => typeof row === 'object' && row !== null); } export async function pullChecks(transport, ref, number, limit = 100) { const pull = (await getPull(transport, ref, number)); const sha = pull.head?.sha; if (typeof sha !== 'string') return []; const runs = await githubPages(transport, `/repos/${ref.owner}/${ref.repo}/commits/${sha}/check-runs`, { limit, key: 'check_runs' }); const statuses = await commitStatuses(transport, ref, sha); return [...runs, ...statuses.map(statusCheck)]; } //# sourceMappingURL=pull.js.map