A Git-compatible version control system written in TypeScript. It reads and writes Git objects,
pack files and refs, speaks the Git fetch/push protocol (v1 and v2), and handles Git LFS, all
without a native git binary. It runs in browsers, Node.js and workers. Storage is pluggable:
memory, a Git directory over any FilesApi, SQL, or a key-value store. A separate orchestration
package combines it with rclone-style file synchronisation.
This repository is a pnpm workspace: the library packages under packages/, runnable examples,
demos and benchmarks under apps/, and a documentation site under docs/.
vcs-workspace (file sync + versioning, via @statewalker/webrun-files-sync)
|
vcs-commands ──────────────┤ vcs-transport-xet ──> vcs-transport-lfs
(Git API) | (large objects over @statewalker/webrun-content-store)
| vcs-transport (+ vcs-transport-adapters)
| |
vcs-store-files / -mem / -sql / -kv
| |
vcs-working-tree (index, checkout, worktree, status)
|
vcs-core (objects, refs, packs, history; storage over @statewalker/webrun-storage)
|
vcs-utils (hash, compression, diff, streams) <── vcs-utils-node (native zlib, Node files)
| Package | What it does | npm |
|---|---|---|
@statewalker/vcs-core |
Git object model (blobs, trees, commits, tags), refs, pack files, History. |
npm |
@statewalker/vcs-working-tree |
Index/staging, checkout, worktree, status, ignore rules, merge/rebase state, WorkingCopy. |
npm |
@statewalker/vcs-commands |
Porcelain Git API: add, commit, branch, merge, rebase, stash, fetch, push, clone, ... |
npm |
@statewalker/vcs-transport |
Git protocol client and server over a duplex stream or HTTP. | npm |
@statewalker/vcs-transport-adapters |
Connects History and storage to the transport's repository interfaces. |
npm |
@statewalker/vcs-transport-lfs |
Git LFS batch protocol with basic whole-object transfer. |
npm |
@statewalker/vcs-transport-xet |
Git LFS xet transfer: sends only missing chunks, falls back to basic. |
npm |
@statewalker/vcs-store-files |
Git .git layout over a FilesApi. |
npm |
@statewalker/vcs-store-mem |
In-memory storage. | npm |
@statewalker/vcs-store-sql |
SQL storage; sql.js adapter included (peer: sql.js). |
npm |
@statewalker/vcs-store-kv |
Storage over a key-value store (IndexedDB, LocalStorage and similar). | npm |
@statewalker/vcs-workspace |
Publish, update, checkpoint and restore: file sync combined with Git history. | npm |
@statewalker/vcs-utils |
SHA-1, CRC32, compression, text/binary diff, delta encoding, streams. | npm |
@statewalker/vcs-utils-node |
Node.js native compression and file adapters. | npm |
@statewalker/vcs-testing |
Shared test suites for storage backends. Private. | |
@statewalker/vcs-integration-tests |
Cross-package tests built from the example apps. Private. |
Public packages ship built ESM and .d.ts files in dist/ (what exports points at) and their
TypeScript sources in src/. Other @statewalker/* dependencies (webrun-files*,
webrun-storage, webrun-content-store, webrun-content-transfer, webrun-files-sync,
webrun-streams*, webrun-http-streams) come from npm.
| Folder | Contents | Run |
|---|---|---|
apps/examples/ |
Eleven numbered Node.js tutorials, from the object model to delta strategies. | pnpm --filter @statewalker/vcs-example-01-quick-start start |
apps/demos/ |
Node.js and browser demos: HTTP server, P2P sync over WebRTC/LiveKit, offline PWA, versioned documents, LFS/xet against HuggingFace. | Node: start; browser: dev (Vite) |
apps/benchmarks/ |
Delta compression, pack operations, realistic repository workloads. | pnpm --filter @statewalker/vcs-benchmark-<name> start |
docs/ |
Observable Framework site: overview, example guide, dependency diagram, release notes. | pnpm docs:dev |
See docs/example-applications.md for each app.
- Use Node.js 24 and enable corepack:
corepack enable. pnpm 10.16.1 is pinned inpackageManager. pnpm installpnpm build. This builds every workspace member that has abuildscript: the packages (rolldown, thentscfor declarations), the browser demos (Vite) and the docs site.pnpm test: vitest in every package and app that has atestscript.- Run something:
It creates an in-memory repository, makes two commits and prints the log.
pnpm --filter @statewalker/vcs-example-01-quick-start start
A minimal program with the porcelain API:
import { Git } from "@statewalker/vcs-commands";
import { createMemoryHistory } from "@statewalker/vcs-core";
import {
createMemoryCheckout,
createMemoryGitStaging,
createMemoryWorkingCopy,
createMemoryWorktree,
} from "@statewalker/vcs-working-tree";
const history = createMemoryHistory();
await history.initialize();
await history.refs.setSymbolic("HEAD", "refs/heads/main");
const staging = createMemoryGitStaging();
const checkout = createMemoryCheckout({ staging });
const worktree = createMemoryWorktree({ blobs: history.blobs, trees: history.trees });
const git = Git.fromWorkingCopy(createMemoryWorkingCopy({ history, checkout, worktree }));
await worktree.writeContent("README.md", new TextEncoder().encode("# Hello\n"));
await git.add().addFilepattern(".").call();
const commit = await git.commit().setMessage("Initial commit").call();
console.log(commit.id);- Git compatibility without Git. Objects, packs and protocol messages are byte-compatible with Git, so repositories written here work with standard Git tools and the other way round. Being pure TypeScript is what lets the same code run in a browser tab, a worker and Node.js.
- History, working tree and commands are separate packages.
vcs-coreonly knows immutable objects and refs. Mutable local state (index, checkout, worktree) lives invcs-working-tree. Multi-step workflows live invcs-commands. A server that only stores and serves repositories does not need the working-tree or command packages. - File sync and versioning do not depend on each other. File synchronisation
(
@statewalker/webrun-files-sync) knows nothing of Git, and the Git packages know nothing of sync. Onlyvcs-workspacecombines them, so either can be used alone. - Storage is a set of small interfaces. Backends implement object, ref and staging stores; pack formats, deltas and object serialization are shared. That is why the same repository logic runs over memory, files, SQL and key-value stores.
- Large files go around the object store. LFS and xet move large objects through a chunked content store with its own ids, keeping them out of packs; the LFS pointer still carries the standard whole-file SHA-256.
- Tests run against sources, consumers against builds. Each package's
vitest.config.tsaliases sibling packages to theirsrc/, so tests never see a stale build. Packageexportspoint atdist/, the same thing npm consumers get. - Internal dependencies use
workspace:^. External ones come from the pnpm catalog inpnpm-workspace.yaml(catalog:; peer ranges from thepeerscatalog), so each dependency has one version across the workspace.
- Apps and
pnpm typecheckneed a build first. Packageexportspoint atdist/. Starting an app beforepnpm buildfails withERR_MODULE_NOT_FOUNDfor a package'sdist/index.js(Node apps) or an unresolved import at dev-server start (browser demos).tscreportsTS2307: Cannot find module '@statewalker/vcs-core' or its corresponding type declarations. After changing a package, rebuild it before running apps against it. pnpm testdoes not run the browser end-to-end tests.pnpm test:appruns them (Playwright, through turbo, which builds first). Install the browsers once withpnpm --filter @statewalker/vcs-demo-browser-app exec playwright install.- Not every store produces Git ids. The standalone tree, commit and tag stores in
vcs-store-mem,vcs-store-kvandvcs-store-sql, and the"sql"history backend, compute their own (FNV-1a) ids instead of SHA-1 object ids. Use thecreate*ObjectStoresfactories of those packages,vcs-store-filesorcreateMemoryHistorywhen ids must match Git. See each store's README. - The HuggingFace demos need network access to
huggingface.co. pnpm lintandpnpm formatrewrite files. Uselint:checkandformat:checkto only report.notes/is not part of the workspace. It is a separate Observable Framework app with its ownpackage-lock.json.
| Command | What it runs |
|---|---|
pnpm build |
pnpm -r run build |
pnpm test |
pnpm -r run test |
pnpm test:app |
turbo run test:app (Playwright app tests) |
pnpm typecheck |
pnpm -r run typecheck |
pnpm lint / pnpm lint:check / pnpm lint:fix |
biome check --write . / biome check . / with --unsafe fixes |
pnpm format / pnpm format:check / pnpm format:fix |
biome format --write . / biome format . / biome check --write --unsafe . |
pnpm docs:dev / pnpm docs:build |
Preview / build the docs site |
pnpm deps:check |
List outdated dependencies (scripts/update-deps.sh --check-only) |
pnpm deps:update (:safe, :interactive) |
Update the catalog and dependencies; :safe also runs tests and build |
pnpm changeset |
Add a changeset to choose a package's bump type or changelog text |
One package: pnpm --filter @statewalker/vcs-core test.
CI runs on every pull request and on main: frozen install, dependency-reference checks, lint,
format, build, typecheck, tests, and checks that every export target exists, that dist/ imports
only declared dependencies and that packed manifests contain no workspace: or catalog:
specifiers. Public packages are published to npm from CI with changesets and npm provenance; see
docs/releasing.md.
| Path | Contents |
|---|---|
packages/* |
Library packages. |
apps/{examples,demos,benchmarks}/* |
Runnable apps. |
docs/ |
Documentation site sources and docs/package-dependencies.md. |
ARCHITECTURE.md |
Background on the Git object model, storage and protocol layers. |
scripts/ |
Dependency update script and debugging scripts for packs and GC. |
pnpm-workspace.yaml |
Workspace globs and dependency catalogs. |
turbo.json |
Task graph for test:app. |
biome.json |
Lint and format configuration. |
LICENSE |
MIT. |