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

@verevoir/sources

v0.5.0

Published

Source-adapter primitive: a contract over remote + local file sources (read/list/tree/write/branch/fork/PR) with implementations as subpath imports. GitHub + local FS + Notion today; GitLab, Bitbucket, S3 follow.

Readme

@verevoir/sources

Source-adapter primitive: a contract over remote file sources (read, list, tree, write, branch, fork, PR open) with implementations as subpath imports.

Purpose

Lets a downstream project read and write files in remote repositories without coupling to a specific source-host SDK. Pick the source via a subpath import; unused implementations don't enter your bundle.

Built for LLM-driven workflows that need API-based code access (no on-disk clones, no resident language servers). The companion package @verevoir/context layers an in-process cache + symbol index on top of these reads.

Most consumers reach this via MCP

If you're driving an LLM agent (Claude Code, custom Anthropic-SDK app, etc.) and want these source operations as tools, you usually don't import @verevoir/sources directly — you run the @verevoir/mcp server, which wraps this contract (via the cached drop-ins from @verevoir/context) and exposes read_file, list_files, get_repo_tree, grep, find_symbol, write_file as MCP tools. See that package's README for Claude Code configuration; the key recommendation is "alwaysLoad": true so the tools surface as first-class instead of being deferred behind ToolSearch.

Direct in-process consumption (the usage shown below) is for: writing your own MCP server, embedding the source surface inside a non-MCP runtime (e.g. server-side action handlers), or building higher-level libraries that compose with adapters.

Subpaths

  • @verevoir/sources — core types, the SourceAdapter contract, the SourceApiError class, and the envFromProcessEnv helper. No source dependency.
  • @verevoir/sources/github — GitHub REST + Git Data adapter. Uses native fetch, no SDK dependency.
  • @verevoir/sources/fs — local filesystem adapter. repoUrl is a local directory path. No auth, no API. Reads/lists/walks/writes; fork + PR throw 501 (not applicable to a local filesystem).
  • @verevoir/sources/notion — Notion adapter via @notionhq/client (optional peer dep). Models a Notion workspace as a documentation tree: pages are files, child pages are subdirectories, content is the page's blocks rendered to Markdown. Ships a minimal Markdown↔blocks converter for round-tripping aigency-generated content; rich Notion features (tables, callouts, etc.) read as best-effort placeholders.

Future adapters land alongside (@verevoir/sources/gitlab, @verevoir/sources/bitbucket, @verevoir/sources/s3) under the same contract.

Install

npm install @verevoir/sources

No mandatory peer dependencies — the GitHub adapter uses native fetch.

Canonical usage — GitHub

import { envFromProcessEnv } from '@verevoir/sources';
import { readFile, writeFile, openPullRequest } from '@verevoir/sources/github';

const env = envFromProcessEnv();
if (!env) throw new Error('GITHUB_TOKEN not set');

// Read a file from the default branch.
const { content, sha } = await readFile(env, 'https://github.com/acme/charts', 'README.md');

// Write a file on a feature branch (branch is created if missing).
await writeFile(
  env,
  'https://github.com/acme/charts',
  'docs/notes.md',
  '# Notes\n\nBody.\n',
  'feature/notes',
  'Add notes'
);

// Open a PR from the feature branch to main.
const prUrl = await openPullRequest(
  env,
  'https://github.com/acme/charts',
  'feature/notes',
  'main',
  'Add docs/notes.md',
  'Body of the PR.'
);

Canonical usage — Local filesystem

Same contract; no auth required. repoUrl is interpreted as a directory path.

import { readFile, listFiles, getRepoTree, writeFile } from '@verevoir/sources/fs';

const env = { token: '', forkOrg: '' }; // FS adapter ignores both

// Walk the working tree (skipping node_modules, .git, dist, etc.).
const tree = await getRepoTree(env, '/path/to/project');
console.log(`${tree.entries.filter((e) => e.type === 'blob').length} files`);

// Read + write the same way as GitHub.
const readme = await readFile(env, '/path/to/project', 'README.md');
await writeFile(
  env,
  '/path/to/project',
  'docs/notes.md',
  '# Notes\n',
  'ignored', // FS adapter ignores branch
  'ignored' // FS adapter ignores commit message
);

ensureFork and openPullRequest throw 501 on the FS adapter — there's no local-FS equivalent. The customer manages git operations themselves.

Fork-pivot pattern

When a writeFile to an upstream repo returns 403 (no write access), the caller can fork the upstream, write to the fork, and open a PR back:

import { ensureFork, writeFile, openPullRequest, SourceApiError } from '@verevoir/sources/github';

try {
  await writeFile(env, upstreamUrl, path, content, branch, message);
} catch (err) {
  if (err instanceof SourceApiError && err.status === 403) {
    const forkUrl = await ensureFork(env, upstreamUrl);
    await writeFile(env, forkUrl, path, content, branch, message);
    await openPullRequest(env, upstreamUrl, `${env.forkOrg}:${branch}`, 'main', 'Title', 'Body');
  } else {
    throw err;
  }
}

The contract

Every subpath exposes the same set of functions (or a strict subset for read-only sources):

readFile(env, repoUrl, path, ref?)         → Promise<{ content, sha }>
listFiles(env, repoUrl, prefix, ref?)      → Promise<DirEntry[]>
getRepoTree(env, repoUrl, ref?)            → Promise<RepoTree>
isFresh(env, repoUrl, path, version, ref?) → Promise<boolean>
writeFile(env, repoUrl, path, content, branch, message) → Promise<void>
ensureBranch(env, repoUrl, branch)         → Promise<void>
ensureFork(env, upstreamUrl)               → Promise<string>
openPullRequest(env, target, head, base, title, body) → Promise<string>
getDefaultBranch(env, repoUrl)             → Promise<string>

isFresh answers "is the version I'm holding still the live one for (repoUrl, path, ref)?" — the cheap freshness check cache layers (@verevoir/context's wrapWithCache) use to validate held entries without re-fetching content. Returns false when the source has moved (including when the path no longer resolves).

The SourceAdapter interface in @verevoir/sources captures this exactly. An aggregate export (e.g. github) is also available per subpath so generic callers can pass an adapter around as a single value.

Errors

SourceApiError is thrown on transport / API failures. status carries the HTTP status when present; detail carries the truncated response body for non-404 errors. 404 is the conventional "ref / path doesn't exist" signal — callers fall back to default-branch reads or other recovery on that status.

What this is NOT

  • Not an Octokit replacement. Functions cover read + write + branch + fork + PR open; everything else (issues, releases, etc.) stays on the source's own SDK.
  • Not a sync engine. Each call is independent; no local working tree, no shadow state.
  • Not a language-aware index. Symbol extraction + content cache live in @verevoir/context on top.

See also

License

Apache-2.0.