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

git-edge

v0.1.2

Published

High-level edge-compatible git operations — parsed-object cache, object-level three-way merge, and repo management utilities. Composes with git-fs-s3.

Readme

git-edge

npm version CI license

High-level edge-compatible git operations on top of isomorphic-git: a parsed-object LRU cache, an object-level three-way merge that never needs a worktree, and repo-cache/init utilities. No node:* imports anywhere in src/ — runs on Cloudflare Workers, Vercel Edge, Deno Deploy, and Node.

Extracted from the same production git-hosting service as git-fs-s3 — the two compose (see below) but neither imports the other; both just agree on isomorphic-git's { fs, gitdir, cache? } shape.

Why

isomorphic-git's own git.merge needs a worktree — a real (or hydrated-to-/tmp) checkout to run its merge driver against. That's a problem for a bare repo living entirely in object storage with no durable disk: merging means materializing the whole tree locally first. threeWayMerge here works directly on the object graph (trees, blobs, commits) — no checkout, no worktree, safe to run inside a Lambda/Worker/Edge function that only ever sees { fs, gitdir }.

Separately, isomorphic-git re-parses a packfile index from scratch on every readTree/log/readObject call unless callers share a cache object across calls — and even with that shared cache, higher-level parsed results (a rendered commit list, a resolved merge base) get recomputed every time. createParsedObjectCache is a generic LRU for exactly those parsed values, keyed however the caller likes.

Install

npm install git-edge isomorphic-git

git-fs-s3 is an optional peer — install it if you need an S3/R2-backed fs to pass in; git-edge itself works with any isomorphic-git-compatible fs (including plain node:fs).

Quick start

import git from "isomorphic-git";
import fs from "node:fs";
import { threeWayMerge, GitMergeConflictError } from "git-edge";

const repo = { fs, gitdir: "/repo.git", cache: {} };

try {
  const { commitOid } = await threeWayMerge(repo, "feature", "main", {
    authorName: "Ada",
    authorEmail: "[email protected]",
  });
  console.log("merged:", commitOid);
} catch (err) {
  if (err instanceof GitMergeConflictError) {
    console.log("conflicts in:", err.conflictingPaths);
  } else {
    throw err;
  }
}

With git-fs-s3

import { createGitFs, MemoryObjectStore } from "git-fs-s3";
import { threeWayMerge, initBareRepo } from "git-edge";

const fs = createGitFs(new MemoryObjectStore());
const repo = { fs, gitdir: "/repo.git", cache: {} };

await initBareRepo(repo);
// ... commits land on "feature" and "main" via git-fs-s3's fs ...
await threeWayMerge(repo, "feature", "main");

API

threeWayMerge(repo, sourceRef, targetRef, opts?)

Merges sourceRef into targetRef at the object level — no worktree.

  • Source is an ancestor of target, or vice versa → fast-forward: just moves targetRef, no merge commit.
  • Otherwise, flattens both trees (deep, recursive) against their merge base, takes non-conflicting changes automatically, and content-merges paths both sides touched with a line-level three-way merge.
  • Throws GitMergeConflictError (with conflictingPaths: string[]) if any file has unresolved conflicts. The conflict markers (<<<<<<< ours / ======= / >>>>>>> theirs) are still written to a blob and included in the (unreached) result tree — a caller that wants "write the conflicted state so a human can resolve it" can catch the error, note the paths, and re-run its own resolution flow rather than losing that information.
  • opts.message, opts.authorName/opts.authorEmail (default "Git Edge" <git-edge@local>).

Returns { commitOid } — the new merge commit, or the fast-forwarded targetRef's new oid.

analyzeMerge(repo, sourceRef, targetRef)

Cheap pre-merge check: resolves both refs and checks ancestry. Returns { canMerge, fastForward, diverged }canMerge: false only means a ref failed to resolve, not that a real merge would conflict (that's only knowable by attempting one; this doesn't walk trees at all). Safe to call before deciding whether to show a "conflicts likely" hint in a UI.

createParsedObjectCache(options?)

A generic in-memory LRU for any JS value, keyed by caller-chosen strings (convention: `${oid}:${format}`).

const cache = createParsedObjectCache({ maxSize: 128 * 1024 * 1024, ttl: 3600_000 });
cache.set(`${oid}:commit`, parsedCommit);
const hit = cache.get<ParsedCommit>(`${oid}:commit`);
cache.invalidatePrefix(gitdir); // drop everything under a repo after a rewrite
  • options.maxSize — byte budget, estimated via JSON.stringify(value).length (default 256 MiB).
  • options.ttl — entry TTL in ms (default 1 h).
  • .invalidatePrefix(prefix) — drop every key starting with prefix; O(cache size), fine for occasional invalidation, not a hot-path operation.

getRepoCache(ownerKey, repoName) / invalidateRepoCache(ownerKey, repoName)

Per-repo isomorphic-git packfile cache object management, keyed `${ownerKey}/${repoName}`. isomorphic-git treats this cache as opaque and safe to share indefinitely (git objects are content-addressed/immutable), so a long-lived per-repo instance turns "reparse this pack's index" from once-per-call into once-per-process. Call invalidateRepoCache after anything rewrites a repo's storage out from under a live process (a rename, a bulk resync) so stale parsed state can't leak into the next read.

initBareRepo(repo, defaultBranch?)

Thin wrapper over git.init({ ...repo, bare: true }) — accepts any fs (git-fs-s3-backed, node:fs, in-memory), defaults defaultBranch to "main".

estimateRepoSize(repo, stat, list)

Sums .pack/loose-object file sizes under objects/. Caller supplies stat/list since "size of a file" isn't part of isomorphic-git's own fs contract — for node:fs that's fs.stat/fs.readdir; for an object-storage-backed fs it's typically a HEAD request per key. Can be expensive against remote storage — prefer tracking size incrementally at write time where possible.

Errors

GitEdgeError — base class. GitMergeConflictError extends GitEdgeErrorconflictingPaths: string[], thrown only by threeWayMerge.

Semantics & limitations

  • threeWayMerge's content merge is a from-scratch line-level three-way merge (not diff3/libgit2), used only for paths both sides changed from the merge base — most changes (added/deleted/single-side-modified) resolve without touching it at all.
  • No binary-file merge support — conflict markers are written as text into whatever bytes the paths held; binary content will produce a nonsensical merged blob, not a clean conflict signal. Detect binary paths upstream if that matters for your use case.
  • estimateRepoSize and the parsed-object cache are general-purpose helpers, not required by threeWayMerge/analyzeMerge — use whichever pieces you need independently.

License

MIT