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

wasm-git

v0.0.17

Published

Wasm-git ======== ([Wasm should be pronounced like `awesome` starting with a `W`](https://youtu.be/C8j_ieOm4vE?t=1644) — as stated in the [WebAssembly Music](https://github.com/petersalomonsen/javascriptmusic) talk at WebAssembly Summit 2020).

Readme

Wasm-git

(Wasm should be pronounced like awesome starting with a W — as stated in the WebAssembly Music talk at WebAssembly Summit 2020).

Git for Node.js and the browser, using libgit2 compiled to WebAssembly with Emscripten.

Imagine that every app you use kept a complete journal of your data: every change recorded, a backup on your own computer that is truly yours, and synchronization between your devices whenever you want it. Software developers have had exactly this for decades — the tool is called Git. Wasm-git brings that same engine to web applications: store user data locally in the browser with full change history, and push/pull to a remote when — and only when — the user chooses. The application doesn't need to expose any Git terminology: a single "synchronize data" button can do all of it behind the scenes.

Quick start

npm install wasm-git

The package ships prebuilt WebAssembly binaries in several variants (see Choosing a variant below). The easiest one to start with is the async variant, which runs on the browser main thread or in Node.js with plain async/await:

import initGit from 'wasm-git/lg2_async.js';

const lg = await initGit();
const FS = lg.FS;

// Git needs to know who you are before committing
FS.writeFile('/home/web_user/.gitconfig',
  '[user]\nname = Your Name\nemail = [email protected]');

// Create a repository, add a file, and commit
FS.mkdir('/myrepo');
FS.chdir('/myrepo');
await lg.callMain(['init', '.']);
FS.writeFile('data.json', JSON.stringify({ hello: 'world' }));
await lg.callMain(['add', 'data.json']);
await lg.callMain(['commit', '-m', 'first commit']);

Cloning and pushing over HTTP works the same way:

await lg.callMain(['clone', 'https://your-git-server.example.com/repo.git', 'repo']);
FS.chdir('repo');
// ...edit files, add, commit...
await lg.callMain(['push']);

When cloning from the browser, the git server must allow cross-origin requests (CORS) — or be served from the same origin. githttpserver is a ready-made reference server, and its live instance at https://wasm-git.petersalomonsen.com/ is an open playground where you can try cloning, editing, committing and pushing directly in the browser (please don't abuse it — it's there for you to test).

The files can also be loaded from public CDNs such as unpkg or jsDelivr (e.g. https://unpkg.com/wasm-git/lg2_async.js).

Choosing a variant

| Variant | File | Where it runs | Persistence | Notes | |---------|------|---------------|-------------|-------| | Sync | lg2.js | Browser: Web Worker only. Node.js: main thread or worker_threads | MEMFS / IDBFS / NODEFS | Smallest binary; synchronous callMain. Needs a worker in the browser because of synchronous HTTP and long-running operations. | | Async | lg2_async.js | Browser main thread or worker; Node.js | MEMFS / IDBFS / NODEFS | Asyncify build: await lg.callMain(...). Larger binary, simplest client code. | | OPFS (pthreads) | lg2_opfs.js | Web Worker, requires cross-origin isolation | OPFS | Fastest OPFS variant; needs COOP/COEP headers for SharedArrayBuffer. | | OPFS (JSPI) | lg2_opfs_jspi.js | Web Worker, no isolation needed | OPFS | SAB-free, smallest OPFS binary; needs a JSPI-capable browser. | | OPFS (ASYNCIFY) | lg2_opfs_async.js | Web Worker, no isolation needed | OPFS | SAB-free universal fallback; largest binary. | | OPFS auto-loader | lg2_opfs_auto.js | Web Worker | OPFS | Picks the best supported OPFS build at runtime and exposes a uniform API — see OPFS variants and the runtime loader. |

For browser persistence with the sync/async variants, mount IDBFS and call FS.syncfs — or use the OPFS variants, which persist through the filesystem itself with better performance and quota management.

Complete working examples for every variant are in the test folders:

Built with wasm-git

  • Ariz-Portfolio — tracks crypto asset balances, profits and losses on the NEAR protocol blockchain. Uses wasm-git to store portfolio data with full change history on the user's own machine, and lets the user download a complete copy — history included — to their computer. Its optional cloud sync is end-to-end encrypted with encrypted-git-storage: repositories are encrypted before upload with keys only the user holds.
  • Y42 — a data pipeline platform (DataOps) where every integration, model and dashboard is versioned in git. In the Data Council talk Using GIT as a NoSQL Database, Y42's founder Hung Dang explains that "under the hood we developed our own git client for the browser using wasm-git, which is a WebAssembly compiled version of libgit2" — and recommends checking out wasm-git at the end of the talk.
  • WebAssembly Music — a music-making environment in the browser and wasm-git's original driving use case: compositions and instruments are stored and versioned locally, and synchronized with a remote when the user chooses. See it in action in this video playlist.
  • AutoDev — an AI-native multi-agent development platform built on Kotlin Multiplatform: wasm-git is the git backend for its WebAssembly target, powering clone, log, diff and status in GitOperations.wasmJs.kt.
  • githttpserver — a CORS-enabled git server you can host yourself, plus the browser playground at https://wasm-git.petersalomonsen.com/ showing basic operations: clone, edit, add, commit, push and pull.
  • encrypted-git-storage — an addon that encrypts and decrypts repositories locally in the browser (AES-256-GCM); the server only ever sees ciphertext. Includes a remote helper for the native git client, so users can clone their own application data — decrypted, with full history — straight to their computer.

Wasm-git is also covered in the book Building and Deploying WebAssembly Apps (BPB Publications, 2025) by wasm-git author Peter Salomonsen.

OPFS usage example

The pthreads OPFS version must run in a Web Worker because it requires SharedArrayBuffer (for pthreads), which in turn requires Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp (or credentialless) HTTP headers.

worker.js (Web Worker):

// Import the OPFS-enabled wasm-git module
const lgMod = await import('./lg2_opfs.js');
const lg = await lgMod.default();
const FS = lg.FS;

// WASMFS doesn't pre-create /home/web_user like MEMFS
try { FS.mkdir('/home'); } catch(e) {}
try { FS.mkdir('/home/web_user'); } catch(e) {}
FS.writeFile('/home/web_user/.gitconfig',
  '[user]\n  name = Your Name\n  email = [email protected]');

// Create an OPFS-backed directory using the WASMFS OPFS backend
const backend = lg._lg2_create_opfs_backend();
const workingDir = '/opfs';
// Use ccall to marshal the JS string to a C pointer
lg.ccall('lg2_create_directory', 'number', ['string', 'number', 'number'],
         [workingDir, 0o777, backend]);

// Clone using absolute path to avoid CWD ambiguity with WASMFS
const repoDir = workingDir + '/myrepo';
lg.callMain(['clone', 'https://github.com/user/repo.git', repoDir]);

// Work around a WASMFS getcwd() bug: create a root symlink so the path
// returned by getcwd() resolves correctly for libgit2's repo discovery.
FS.symlink(repoDir, '/myrepo');
FS.chdir(repoDir);

// Re-set CWD before each callMain since WASMFS may reset it
FS.chdir(repoDir);
FS.writeFile('newfile.txt', 'Hello OPFS!');
FS.chdir(repoDir);
lg.callMain(['add', 'newfile.txt']);
FS.chdir(repoDir);
lg.callMain(['commit', '-m', 'Add new file']);
FS.chdir(repoDir);
lg.callMain(['push']);

postMessage({ done: true });

See test-browser-opfs/worker.js for a complete working example.

OPFS variants and the runtime loader

OPFS's open/dir/metadata operations (getFileHandle, getDirectoryHandle, removeEntry, entries, createSyncAccessHandle) are async-only. To call them from libgit2's synchronous file IO, wasm-git ships three OPFS builds that bridge async→sync in different ways:

| Build | File | Bridge | SharedArrayBuffer / cross-origin isolation | wasm size | Relative speed | |-------|------|--------|--------------------------------------------|-----------|----------------| | pthreads / WASMFS | lg2_opfs.js | WASMFS OPFS backend blocks a worker thread via Atomics.wait | Required (COOP/COEP) | ~920 KB | fastest | | JSPI | lg2_opfs_jspi.js | JSPI — native WebAssembly stack switching | Not required | ~805 KB | fast | | ASYNCIFY | lg2_opfs_async.js | Asyncify — wasm rewritten to unwind/rewind its stack | Not required | ~1.5 MB | slower (instrumentation overhead) |

(wasm sizes are for -O3 release builds and will drift; JSPI is the smallest because it needs no stack-rewriting instrumentation, ASYNCIFY the largest for the same reason.)

The pthreads build needs Cross-Origin-Opener-Policy / Cross-Origin-Embedder-Policy headers (cross-origin isolation), which some hosts (e.g. NEAR web4) cannot set. The JSPI and ASYNCIFY builds are SAB-free: they persist to OPFS by suspending the wasm stack across the async OPFS calls, so they run with self.crossOriginIsolated === false. All three must run inside a Web Worker (OPFS sync access handles require one).

Runtime loader (lg2_opfs_auto.js)

lg2_opfs_auto.js picks the most optimal supported build at runtime and exposes a uniform git API over all three. Selection order:

  1. pthreads when the page is cross-origin isolated (crossOriginIsolated === true and SharedArrayBuffer exists);
  2. JSPI when available (WebAssembly.Suspending / WebAssembly.promising);
  3. ASYNCIFY otherwise (universal fallback).

If OPFS itself is unavailable (navigator.storage.getDirectory missing, insecure context), selectOpfsVariant() returns null — fall back to the non-OPFS IDBFS build (lg2.js).

// inside a Web Worker (type: 'module')
import { loadOpfsGit, selectOpfsVariant } from './lg2_opfs_auto.js';

console.log(selectOpfsVariant());      // 'pthreads' | 'jspi' | 'asyncify' | null

const git = await loadOpfsGit({ user: 'Your Name', email: '[email protected]' });
console.log(git.variant);              // which build was loaded

await git.clone('https://example.com/repo.git', 'repo.git');
await git.writeFile('repo.git', 'a.txt', 'hello');
await git.addCommitPush('repo.git', 'a.txt', 'add a.txt');
console.log(git.readFile('repo.git', 'a.txt'));   // 'hello'

// After a reload, restore an existing repo from OPFS before using it:
await git.syncRepo('repo.git');

The detection helpers (detectOpfsEnvironment, selectOpfsVariant) are pure functions that accept an env object, so they can be unit-tested or forced (see test/opfs-detect.spec.js).

See test-browser-opfs-noniso/worker.js for a complete worker built on the loader.

Filesystem backends

Wasm-git supports multiple filesystem backends for different use cases:

MEMFS (Memory File System)

  • Use case: In-memory storage, not persisted
  • Build target: Default (./build.sh Release)
  • Browser support: All browsers

IDBFS (IndexedDB File System)

  • Use case: Browser persistent storage using IndexedDB
  • Build target: Default (./build.sh Release)
  • Browser support: All browsers with IndexedDB

NODEFS (Node.js File System)

  • Use case: Node.js native filesystem access
  • Build target: Default (./build.sh Release)
  • Platform: Node.js only

OPFS (Origin Private File System)

  • Use case: Modern browser persistent storage with better performance and quota management
  • Browser support: Chrome 86+, Edge 86+, Firefox 111+, Safari 15.2+ (JSPI variant: Chromium-based browsers with JSPI)
  • Advantages: Better performance and quota compared to IDBFS
  • Requirements: Must run in a Web Worker (OPFS sync access handles require one)
  • Three variants — see OPFS variants and the runtime loader:
    • pthreads / WASMFS (./build.sh Release-opfslg2_opfs.js): synchronous callMain; requires cross-origin isolation (Cross-Origin-Opener-Policy: same-origin + Cross-Origin-Embedder-Policy: require-corp/credentialless) for SharedArrayBuffer.
    • JSPI (./build.sh Release-opfs-jspilg2_opfs_jspi.js): SAB-free; async callMain; smallest binary.
    • ASYNCIFY (./build.sh Release-opfs-asynclg2_opfs_async.js): SAB-free; async callMain; universal fallback, largest binary.
    • lg2_opfs_auto.js selects the best supported variant at runtime.

Building and developing

Compatibility

  • libgit2: v1.9.4
  • Emscripten: Pinned to 6.0.3
  • Node.js: v18+
  • Browsers: Modern browsers with WebAssembly support

Prerequisites

Quick Start

  1. Clone the repository

    git clone https://github.com/petersalomonsen/wasm-git.git
    cd wasm-git
  2. Install and activate Emscripten

    git clone https://github.com/emscripten-core/emsdk.git
    cd emsdk
    ./emsdk install 6.0.3
    ./emsdk activate 6.0.3
    source ./emsdk_env.sh
    cd ..
  3. Set up libgit2

    ./setup.sh

    This script downloads libgit2 v1.9.4 and applies necessary patches for WebAssembly compilation.

  4. Build the project

    cd emscriptenbuild
    ./build.sh         # Debug build (smaller, for development)
    ./build.sh Release # Release build (optimized)

    For async versions (with Asyncify support):

    ./build.sh Debug-async   # Debug async build
    ./build.sh Release-async # Release async build

    For OPFS versions:

    ./build.sh Release-opfs       # pthreads / WASMFS OPFS build (needs COOP/COEP)
    ./build.sh Release-opfs-jspi  # SAB-free OPFS build using JSPI
    ./build.sh Release-opfs-async # SAB-free OPFS build using ASYNCIFY
    # (Debug-opfs, Debug-opfs-jspi and Debug-opfs-async also exist)
  5. Install npm dependencies

    npm install
  6. Run tests

    npm test                         # Run Node.js tests
    npm run test-browser             # Run browser tests (sync version)
    npm run test-browser-async       # Run browser tests (async version)
    npm run test-browser-opfs        # OPFS pthreads/WASMFS tests (cross-origin isolated)
    npm run test-browser-opfs-noniso # SAB-free OPFS tests (ASYNCIFY + JSPI, non-isolated)
    npm run test-opfs-detect         # Loader variant-detection unit tests
    npm run test-opfs-loader         # Multi-browser loader selection tests (Chromium/Firefox/WebKit)

Development Options

GitHub Codespaces

The easiest way to get started is using GitHub Codespaces. The repository includes a .devcontainer configuration that automatically sets up the complete development environment with all dependencies.

Local Development

The Github actions test pipeline shows all the commands needed for CI/CD and can be used as a reference for local setup.

Build Outputs

After building, you'll find the following files in emscriptenbuild/libgit2/examples/:

  • lg2.js and lg2.wasm - Synchronous version
  • lg2_async.js and lg2_async.wasm - Asynchronous version with Asyncify
  • lg2_opfs.js and lg2_opfs.wasm - OPFS version with WASMFS + pthreads
  • lg2_opfs_jspi.js and lg2_opfs_jspi.wasm - SAB-free OPFS version using JSPI
  • lg2_opfs_async.js and lg2_opfs_async.wasm - SAB-free OPFS version using ASYNCIFY

The runtime loader lg2_opfs_auto.js (a hand-written module, not a build output) selects the best of the three OPFS builds at runtime — see OPFS variants and the runtime loader.

These files are also available from npm packages and CDNs for production use.

Troubleshooting

  1. Build errors: Ensure the Emscripten environment is properly activated:

    source /path/to/emsdk/emsdk_env.sh
  2. Test failures: Remove any stale test directories before running tests:

    rm -rf nodefsclonetest
    npm test

History

Wasm-git started in 2020, and getting libgit2 to run in a JavaScript environment required several fixes to Emscripten itself, all merged upstream that year: #10095, #10526, #10782 and #10669 (NODEFS support).