@osdlc/code-host
v0.7.7
Published
The CodeHost port: what a code host (GitHub, Forgejo) answers about pull requests, with GitHub and Forgejo adapters, a host selector keyed on the git remote, doctor auth probes, and PR-reference parsing. Every shell-out goes through an injected command ru
Maintainers
Readme
@osdlc/code-host
The CodeHost port: what a code host must answer about pull requests, plus the GitHub and Forgejo adapters behind it.
Callers that need PR facts (a classifier, a router, an orchestrator) ask the
port instead of calling gh or a REST API themselves. Supporting another host
means writing a new adapter, not changing every caller.
Install
npm install @osdlc/code-hostESM only. Node.js ^22.22.2 || ^24.15.0 || >=26.0.0. This is the range of its
dependency @osdlc/typed-git, which takes it from hosted-git-info.
pnpm add @osdlc/code-host and yarn add @osdlc/code-host work too.
Quick start
import { selectCodeHost } from '@osdlc/code-host'
import { spawnRunner } from '@osdlc/command-seam'
import { describe, FailureError } from '@osdlc/result'
// Picks the GitHub or Forgejo adapter from the repository's `origin` remote.
const selected = selectCodeHost(process.cwd(), { git: spawnRunner('git'), gh: spawnRunner('gh') })
if (!selected.ok) throw new FailureError(selected.error)
const host = selected.value
const pr = host.getPr(42, { since: null })
if (pr.ok) console.log(pr.value?.title, pr.value?.mergeable)
else console.error(describe(pr.error)) // "gh is not signed in to GitHub. Run `gh auth login`."
host.postComment(42, 'Rebased onto main.')Tests pass a fake CommandRunner instead, so nothing touches the network.
Failures
A query that can fail returns a Result<T, CodeHostFailure> from
@osdlc/result. The failure has a kind to branch on, a
message and a hint for a person, the host that failed, and the tool's
own output as detail. Both adapters report the same kinds, so a caller
handles "not signed in" once.
A declared absence is still a success. getPr of a number that does not
exist, and prForBranch of a branch with no PR, return
{ ok: true, value: null }. On Forgejo, when the comments, reviews or status
call inside getPr fails, getPr returns a failure, never a PR with no
checks or no reviews.
| kind | Means | hint |
| --- | --- | --- |
| not-installed | gh or curl is not on PATH | GitHub: install gh and run gh auth login; Forgejo: "Install curl." |
| not-authenticated | gh is signed out, or Forgejo answered 401 or 403 | "Run gh auth login." / "Set FORGEJO_TOKEN to a valid token." (the variable forgejoTokenEnv names) |
| network | the host could not be reached (DNS, connect, TLS) | "Check your network connection ..." |
| not-found | no such repository, or hidden from this account (404) | "Check the repository name ..." |
| rate-limited | the API limit was reached (429) | "Wait a few minutes and try again." |
| timeout | the tool or request ran out of time | |
| http | any other HTTP error status; the status is in status and detail | |
| unreadable | the host answered, but the answer could not be read | |
| unsupported | the host has no such feature (reviewThreads on Forgejo) | |
| no-remote, bad-remote, missing-token | selectCodeHost could not build a Forgejo adapter | |
| not-executable, bad-cwd, output-cap, signal, aborted, spawn, exit | the subprocess layer's own kinds, passed through | |
On GitHub the failure's cause is @osdlc/typed-gh's GhFailure. On Forgejo
it is @osdlc/command-seam's CommandFailure for a curl that failed.
A mutation (postComment, updateBranch, ...) throws on failure instead. It
throws a FailureError carrying the same CodeHostFailure, or a
CommandFailed. Both are FailureErrors, so @osdlc/cli-tool's runMain
prints them the same way. Three throws are plain Errors instead:
setDraft, mergePr and createPr throw CodeHostUnsupported on Forgejo,
updateComment throws CommentNotFoundError when the comment is gone (HTTP
404), and GitHub's createPr throws an Error when gh prints a URL naming
no PR.
selectCodeHost returns a Result<CodeHost, CodeHostFailure> too (see
D-01).
Runtime and platform
Node.js ^22.22.2 || ^24.15.0 || >=26.0.0, ESM only. Bun also runs it, through the compiled
dist/. No Bun.* API is used. The GitHub adapter needs the gh CLI on PATH, signed in,
at version 2.48 or later: listComments and the review comments getPr folds into activity read
every page with gh api --paginate --slurp, and --slurp is new in 2.48. On an older gh,
listComments fails and getPr reports no review comments. The
Forgejo adapter needs curl and a personal access token in FORGEJO_TOKEN (or the variable named
by the forgejoTokenEnv option). Both need git for drift counts. Linux and macOS are the tested
platforms.
What is here
| Export | What it is |
| --- | --- |
| CodeHost | The port: listPrs, listPrDetails, getPr, postComment, updateComment, listComments (optional), setDraft, updateBranch, reviewThreads, mergePr, createPr, parsePrUrl, prForBranch. Every method is synchronous. Queries return a Result |
| CodeHostFailure, CodeHostFailureKind | The classified failure the queries report (see Failures) |
| CodeHostUnsupported, CommentNotFoundError | The Errors a mutation throws for a verb the code host lacks, and for a deleted comment |
| CodeHostPr, PrFacts, Activity, PrInput, ... | The normalized shapes the port speaks (types.ts) |
| GithubCodeHost, codeHostPrFrom | GitHub adapter over @osdlc/typed-gh |
| ForgejoCodeHost, forgejoPrFrom | Forgejo adapter over curl and the Forgejo REST API. Reads its token from FORGEJO_TOKEN by default. forgejoTokenEnv names another variable |
| selectCodeHost, resolveCodeHost, codeHostNameForRemote | Pick the adapter from the git remote. selectCodeHost returns a Result |
| githubAuthChecks, forgejoAuthChecks, authChecksFor | The auth checks a doctor command runs |
| assemblePrFacts | Adds local-git drift (behind/ahead) to a CodeHostPr |
| parsePrInput | Parse a PR argument: a number, #n, owner/repo#n, or a URL (its PrRef keeps the URL's host) |
Boundaries
- Every shell-out goes through a
CommandRunnerfrom@osdlc/command-seamthat the caller passes in.selectCodeHostbuilds a default runner only forcurl. Tests pass fakes, so the tests never touch the network. - Git and GitHub access goes through
@osdlc/typed-gitand@osdlc/typed-gh. - No verdicts. Classifying a PR stays with the caller; this package only produces the facts it reads.
Not here yet
The port has no write verbs beyond postComment, updateBranch, createPr,
setDraft, updateComment and mergePr. The Forgejo adapter does not
implement createPr, setDraft or mergePr yet. editPrBody, postReview
and replyToThread are the next additions.
When not to use
- You only target GitHub and want the full API surface: use octokit.
- You run one-off scripts: call
ghdirectly. - You need an async API: every method here is synchronous by design.
- You need a host other than GitHub or Forgejo (GitLab, Bitbucket): there is no adapter yet.
How it compares
From the author's knowledge, and possibly out of date. octokit is the
complete GitHub client, but it covers only GitHub and handles credentials
itself. The gh CLI covers GitHub only and has no stable programmatic
interface beyond its --json output. This package is a small port with two
adapters, so a caller written against CodeHost does not change when the host
does. It reads no token for GitHub (gh finds its own). It runs every
subprocess through a runner the caller passes in, so callers can be tested
offline.
Licence
MIT OR Apache-2.0, at your option. See LICENSE-MIT and LICENSE-APACHE.
