npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@volter/twin-github

v2.0.9

Published

Local GitHub twin — PR-evidence REST; your real `@octokit/rest` talks to it unmodified. Mirror, simulate, and fork. Built on @volter/world-core.

Readme

@volter/twin-github

The GitHub twin — a local replica of the GitHub REST API (repositories, git data, issues, pull requests, Actions, releases, organizations and teams, Apps, Pages, security alerts and more) on the shared @volter/world-core kernel. The real @octokit/rest client works against it unmodified.

Note: both planes carry content. Local simulator/fork writes carry full content (a created PR has a title/body/diffs), and the connector's pull path folds the CONTENT real GitHub returns — PR/issue title/body/state, review bodies, comment bodies, and the inline diff findings. The one GitHub surface still unbuilt by design is bulk pull of actual repository code/file contents (see Coverage below).

Surface

  • REST API (github-twin.ts; HTTP wrapper github-server.ts → createGithubTwinServer): list/get PRs, issues, statuses/checks, milestones, and Actions (workflows/runs/jobs; dispatch/rerun/cancel writes), plus Gists (create/edit/list/get/delete and comments).
  • Writes (applyGithubWrite): create PR, edit PR, submit review, issue comment — local transactions (R3/R5/R18); the --read-only flag rejects writes. A review or review comment the twin authors is served with an author: the twin ledgers one caller identity for a local write (actor: { kind: 'agent' }, which carries no login), so the login it serves is the stable twin with type: "User" — the same name the jira twin gives a local writer. A rehearsal reviewer's REAL login rides in the review or comment body by convention; the twin invents no header for it and never guesses a human's account from an unauthenticated write. (Making review.user reflect a genuinely authenticated actor is still the open github.pulls.review_actor_identity todo.)
  • Every review comment names its review. Real GitHub wraps every inline comment in a review, and so does the twin: a comment posted under a review (POST .../pulls/:n/reviews with a comments array) carries that review's id, and a comment posted on its own carries the PR's implicit review — one deterministic id per (repo, PR). It is synthesized on GET .../pulls/:n/reviews from the comments that name it and never stored as a review of its own; the pull request's review count counts it under exactly that condition, so the count and the list describe one population (real GitHub serves the wrapper on /reviews and counts it too). The point is that the join a consumer does (pull_request_review_id → the reviews page) is live against the twin's own data; it used to be dead, because the twin answered pull_request_review_id: null on every review comment it served. The wrapper's id band (0x80000000…) separates it from the twin's own local-sequence and observed-hash id spaces, and that is all it does — real GitHub's review ids reach that range too, so the band is no argument about vendor ids. The invariant that actually holds is that a vendor id is never a twin id: every pulled review and comment is rehashed before it is served, and no review id is ever sent back out (a push addresses a vendor comment by the external_id the pull recorded).
  • Repository webhooks (github-events.ts): register through native /repos/{owner}/{repo}/hooks CRUD. The server resolves stored subscriptions on every write, sends HTTP callbacks and stores their outcomes in the same World. Hook configuration and delivery history survive server restart; there is no separate registration helper. JSON and form payloads carry X-GitHub-Event, X-GitHub-Delivery and, when configured, HMAC-SHA256 over the transmitted bytes. PR, review and comment events carry their source records, including deleted comment snapshots and inline comment parents. Native writes name the modeled twin sender separately from the source author; PR IDs are stable modeled numbers and local PR update times come from their write history. Unknown imported authors and update times remain unknown. Review webhook states use lowercase, while REST states retain uppercase. Imported comments retain their stored identity on edit/delete; known vendor edit times survive the projection. Local inline comments and replies retain their target commit. PR synchronization snapshots are captured with their state writes, before callback delivery, so a later push cannot replace an earlier event's head. Other emitted event types retain partial payload coverage. Native ping, delivery list/detail and explicit redelivery routes are supported; redelivery keeps the original GUID and payload with current hook settings. Failed callbacks are recorded without undoing the vendor mutation. Callback attempts run before the mutation response, with a ten-second timeout per hook; automatic retries and crash recovery of pending attempts are not modeled. Signing secrets are masked on native reads and the mirror. Full hook PATCH removes an omitted secret; config PATCH preserves it. TLS verification remains enabled (insecure_ssl: 1 is explicitly unsupported). Identical configurations with overlapping subscriptions are refused on create or update. Delivery records are local execution history and cannot be deployed to GitHub. See GitHub's repository webhook API and delivery guidance.
  • Conformance (github-conformance.ts): field-name subset vs the vendored GitHub REST PR schema (PR + nested base/head/repo).
  • UI mirror (github-mirror-ui.ts): a GitHub-style PR browser (React) with a per-repo selector ("multiple githubs"), a pure frontend (R3) — the shell + assets with the twin's own fetch adapter mounted beside them. It reads the world through the twin's store door GET /twin/store/mirror (the one named projection github-mirror-state.ts builds server-side; observed review/comment counts are surfaced honestly), and any console write goes through the REST API on the same origin. On that one host a document navigation (Accept: text/html) is the console's deep link and everything else is the vendor's wire; the git smart-HTTP plane is not carried there. Pure helpers both sides share (the store path, the PR/issue route grammar) live in github-shared.ts.
  • Connector (github-connector.ts): the live-vendor lifecycle over an injected octokit-like executor. syncGithubFromReal pulls a repo's PRs + issues and folds their content; the kernel performs local writes through performGithubAction. Pull is per-row conflict tolerant: if one stored event has diverged from what a resource now folds to (a Conflicting duplicate — e.g. a post-append line mutation raced the live writer), that ONE row is skipped rather than aborting the whole repo's fold, so every other resource in the same pull still folds. The result reports it — { observed, deltasAppended, eventsAppended, issues, conflictsSkipped, conflictingIds } — so a poller can log the integrity problem loudly instead of it being swallowed. Every other append error stays fatal. A PR's conversation pulls as subjects, not as a summary: each review is review / <owner/repo>#<n>:review:<reviewId> (author login, human-or-bot, state, body, submitted_at, commit_id, and the INLINE diff comments it wrapped — joined by each /pulls/{n}/comments row's pull_request_review_id), and each issue comment is issue_comment / <owner/repo>#<n>:ic:<commentId>. Two reviews between two polls are therefore two deltas, and each is dated by the PROVIDER (a review by submitted_at, a comment by updated_at ?? created_at), not by the poll clock. The PR keeps its own summary fields, so a consumer reading only pull_request is unaffected. Budget: those three per-PR reads are skipped for a PR whose updated_at has not moved since the last observation (the stamp is read back from the shadow), so N unchanged PRs cost one list call; an open-only pull also sweeps a bounded page (20) of recently-updated CLOSED PRs so a merge is observed. All three conversation reads are PAGED at the vendor's maximum (100/page, bounded at 10 pages), so a busy PR's 31st review, 31st finding or 31st issue comment is observed rather than lost at a page boundary — the last of those is what commentCount publishes, which used to report the truncated 30 as if it were the whole conversation tab. reviewCount and latestReview are drawn from exactly one set — reviews that are submitted and not PENDING — so a count and a verdict can never describe different populations of the same PR.
  • A review nobody showed us says so. Two shapes of inline comment name no review this pull can resolve, and NEITHER is dropped. A comment whose pull_request_review_id is missing entirely (a row the vendor left unattached) rides one fixed bucket per PR, <owner/repo>#<n>:review:unattached; a comment naming a review whose ROW never arrived (a review deleted between the two reads, a page boundary) rides a subject keyed by that id. Both carry partial: true and nothing else — no state, no author, no verdict, because nobody showed us one. partial is what tells a consumer this is an unshown review rather than one whose fields have not arrived yet, and neither is counted or eligible to be latestReview.
  • A finding is authored by whoever wrote it, not by the review that wraps it. Every inline comment carries its OWN authorLogin/authorType, read from that row's user: a scanner App's finding inside a human's review reads as bot, a threaded reply reads as its replier, and the unattached bucket's comments keep their authors although the bucket itself has none. The wrapping review's author is the fallback only for a row that named nobody (user: null); when neither names anyone, no author is served rather than a guess.
  • authorType is a one-way signal. authorType: 'bot' ⇒ a machine wrote it is RELIABLE (only a GitHub App account is typed Bot or suffixed [bot]). The converse is NOT: a GitHub App acting through a user token, a service/machine account, and an Organization all come back typed User with an ordinary login, and a review by a deleted account answers user: null (no login and no type at all). Gate on bot; never read user as "a person did this".
  • Push: a threaded reply is a reply. A local in_reply_to write crosses on GitHub's own reply route (POST .../pulls/{n}/comments/{comment_id}/replies), addressing the root by the id the vendor gave it — resolved from what a pull observed, or from what an earlier push in the same sweep returned. The connector NEVER forwards a twin-minted id to a real account: a reply whose root has no known GitHub id is refused. The kernel stops the deployment at the first failed action and leaves it unconfirmed; later actions wait until the failure is resolved. A successful root comment adopts the vendor ID before a subsequent reply resolves it.

CLI

world-github serve [--read-only] [--port N] [--root DIR]
world-github mirror [--port N] [--root DIR]
world-github conformance [--root DIR]

Point the real @octokit/rest Octokit at it via baseUrl.

Locally created PRs use the modeled authenticated self account, octocat, matching GET /user. Pulled PRs retain the observed author; an unknown author is omitted. Older local PRs recover that same modeled author only when their retained history contains an explicit agent pull_request.create, without changing stored records. This does not add credential-specific review identity: the separate review-writer limitation described above still applies. PR responses include base.repo.full_name and the synthetic node ID PR_<owner/repo>#<number>, shared with the twin's GraphQL API. These node IDs address twin state and are not real GitHub node IDs.

Interaction surfaces

  1. SDK/API — zero edits (preferred): GITHUB_TWIN_URL=http://127.0.0.1:PORT node --require @volter/world-core/inject your-app redirects the real @octokit/rest from api.github.com to the twin. Or override directly: new Octokit({ baseUrl: 'http://127.0.0.1:PORT', auth: 'twin' }).
  2. API + CLI — run the twin in a World and inspect it with volter world log and diff. Use the deployment guide for changeset review and deployment.
  3. Read-only — world-github serve --read-only: unlimited local reads, no rate limits; writes refuse like GitHub (4xx).
  4. UI mirror — world-github mirror renders a GitHub-style view of the twin's state.

(See Getting Started → "Twin interaction surfaces", and cookbook/zero-edit-inject.)

Coverage

Goal: honest, explicitly tracked coverage of GitHub's core feature surface. The only there are no exclusions. Tracked in three buckets — anything not done or carved out is a gap to close.

Done — PRs (create/edit/merge/list/get; full PR/issue content on pull — title/body/state and review/comment bodies fold from real GitHub, plus full content on local writes), first-class issues (pulled from real GitHub via GET /issues, PRs excluded), shared issue/PR number space (one per-repo counter, real GitHub behavior), reviews + diff-anchored review comments (write, list, and reply in a thread — POST .../pulls/:n/comments/:cid/replies inherits the root comment's anchor and answers with in_reply_to_id), issue comments, commit statuses, check runs, requested reviewers, milestones, labels/assignees, PR files/commits (local writes), Actions (workflows + workflow runs + jobs/steps — register/ list/get workflows, workflow_dispatch → queued run, rerun/cancel transitions, run filters status/branch/event, run jobs; rendered in the UI mirror Actions view), Actions secrets/variables (repo + org scope — secret values never returned, mirroring GitHub), Actions caches (list/delete by id or key), Actions artifacts (register/list/get/delete metadata), Actions run logs (302 redirect to a signed URL; serving the bytes is a todo), Git Data API (refs create/get/update/delete, commits, trees, blobs with content, annotated tags), deployments + deployment statuses (create/list/filter + status history), environments (create/update/delete + protection-rule summaries; rendered in the UI mirror Deployments view), org repositories (GET /orgs/:org/repos with type/sort filters), issue pin/unpin, pagination, webhooks, UI mirror (rung-5 ✅), connector (pull content+issues + push: create/edit/ merge/review/comment/status/check/milestone/reviewers), Gists (create/edit/list/get/delete + comments).

Draft PRs become ready through GitHub's GraphQL markPullRequestReadyForReview mutation and return to draft through convertPullRequestToDraft. Both preserve the PR number, head and reviews and emit pull_request with action ready_for_review or converted_to_draft. Invalid selections, unknown nodes, closed PRs and requests for the current draft state fail without changing stored state. The payload subset is clientMutationId and pullRequest { id number isDraft url }. REST PR updates ignore the unsupported draft field; creation still accepts it. Deploying a draft-state change resolves the destination PR's own node ID and uses the matching GraphQL mutation, confirming the returned state before acknowledging it.

Git smart HTTP. Unmodified Git clients clone and push through the portable JavaScript Git implementation in @volter/world-core/git. Git Data REST and smart HTTP share the object store and refs. Real Git is the test oracle; serving does not spawn Git subprocesses. Pushes update REST refs and open PR heads and emit push webhooks. Only refs backed by stored objects are advertised.

PR merges check required statuses and check runs before updating the base ref. Three-way merges preserve non-overlapping file and text changes, reject conflicting changes, and compare the base tip again while holding the ref lock. Armed auto-merge runs on a protected branch when its declared required checks turn green. A newer failure supersedes an old success, and drafts stay open. Merge commits preserve two parents; squash and the modeled rebase method fold the result into one commit. Rename detection, binary conflict resolution, and general Git rebase semantics are not modeled.

SDLC control plane for merge gates — commit statuses at a sha, check runs, branch protection that persists required_status_checks (contexts/strict) + enforce_admins and serves them back (GET /branches/:b summary with real enforcement_level semantics, GET/PATCH .../protection/required_status_checks, POST/DELETE .../protection/enforce_admins), PR reviews that bind commit_id to the exact head sha at submission (a later push moves the PR head, the review stays), and the GraphQL repository.pullRequest(number) fields gh pr view --json reads (headRefOid/state/baseRefName/statusCheckRollup/closingIssuesReferences/changedFiles/ labels/body/comments/assignees/reviewRequests) — scoped to the exact calls this repo's own governance scripts make (human-approval-gate, break-glass-gate, agent-propose, finalize-agent-review, review-prerequisites), so the OA merge-gate logic can one day run hermetically against this twin.

Planned (known-missing, will do) — Projects v2, branch protection rulesets, orgs/teams/members (membership), the rest of the GraphQL API (the modeled subset now includes the gh pr view merge-gate fields), security (code-scanning/dependabot/secret-scanning), packages/pages/codespaces, git-plane follow-ons (full protection enforcement on receive-pack and the remaining PR policies, shallow/partial clone asserts, LFS, SSH transport). (Actions log bytes stay out — CI-compute output, see Out of scope.)

Out of scope (deliberately not modeled, with reason) —

  • ~~Actual repository code / file contents~~ narrowed 2026-08-20: the git plane now stores REAL blob/tree/commit bytes for repos exercised through Git Data REST or git push. What remains unbuilt is bulk pull of a real remote repository's full object graph through the connector (large + redundant for an evidence twin); the API objects AND locally-authored bytes are covered.
  • Running real CI compute (executing Actions workflows): infra, not the API — the run/job objects are covered.

Rate budget — the fail-closed backstop on live calls

liveGithubExecute is the one place this pack issues a live request, so every call it makes is charged against a persistent, fail-closed spend ledger before the request goes out. Past the ceiling, or while a Retry-After/429 cooldown is armed, it throws instead of calling. The ledger is keyed by vendor and a hash of the credential (limits are per credential, so it is deliberately not cwd-scoped) and persists across processes, so a fresh process does not get a fresh allowance; a corrupt ledger counts as a full window rather than zero spend. There is no option to disable it, and no value you can pass for budget that yields an unguarded client — an injected budget is validated by method identity, so a subclass or a Proxy that replaces checkBudget is refused.

The declared numbers: 600 points / hour, using GitHub's own published point scheme: a GET/HEAD/OPTIONS is 1 point. Bounded on every published axis at once — ≤12% of the 5,000 requests/hour primary limit; under the 900-points-per-minute secondary limit even if the whole hour were spent instantly. Writes are charged 8 rather than GitHub's own 5, the single departure from its table and a tightening: at 5 the hour-long window would admit 120 writes inside one minute, 1.5× the documented 80 content-generating requests/minute.

The mechanism is shared and vendor-agnostic — it lives in the kernel (@volter/world-core → packages/world-core/src/rateBudget.ts); what lives here in src/github-budget.ts is this vendor's declaration (window, ceiling, per-endpoint weights, and a reason citing the limits above) plus the vendor-bound GithubBudget. The rule is ratified as ../../../docs/contributing/architecture.md D8, and the kernel module's header documents what the guard does not guarantee — read that before trusting it.

Contents API writes in Git-backed repositories create real blobs, trees and commits on the selected branch. Reads of contents and README files honor branch, tag and commit references; clone/fetch sees the same bytes. Updates and deletions require the current blob SHA, and a competing branch update refuses with 409. Repositories represented only by connector observations retain their observed-file behavior.

Discussion seeding uses GitHub GraphQL repository.discussionCategories and createDiscussion; readback and addDiscussionComment share the same stored discussion. Category lookup supports forward first/after pagination. The remaining GraphQL surface is partial; unsupported fields fail loudly. See GitHub’s schema. Category fields are limited to ID, name, slug, description and answerability; emoji is not modeled. Creation emits the existing discussion webhook through the normal server delivery path.