# Overview

> The four-package @dev-bench gitapp stack.

Source: https://gitapp.n.yatsyk.com/docs/core

# Gitapp

Gitapp is a layered stack that lets an app store its data in a real git
repository — every write is a commit, conflicts are exposed as conflicts,
and any provider that speaks git over HTTPS (GitHub, Gitea, GitLab,
self-hosted) is a valid backend.

The stack is split across four packages so apps can opt in to only what
they need. The boundaries are stable: each layer below is implementable
without knowledge of the layer above.

| Package                | Layer     | Role                                                                                        |
| ---------------------- | --------- | ------------------------------------------------------------------------------------------- |
| `@dev-bench/docstore`  | interface | `DocStore` contract + Zod schemas. No git knowledge.                                        |
| `@dev-bench/git-fs`    | 1         | Git/FS primitives. Browser/Node/in-memory adapters; GitHub/Gitea/GitLab REST.               |
| `@dev-bench/gitapp`    | 2         | Wires `git-fs` into a `DocStore` implementation: transactions, conflict policies, watch.    |
| `@dev-bench/gitapp-ui` | 3         | RN + react-native-web theme, primitives, and full-fledged components driven by view-models. |

## Install

```bash
pnpm add @dev-bench/docstore @dev-bench/git-fs @dev-bench/gitapp
# UI is optional — peer-deps on react / react-native / react-native-svg
pnpm add @dev-bench/gitapp-ui
```

## Conceptual model

A `DocStore` is a transactional document store. Every mutation goes
through `runTransaction`, which is atomic — all mutations apply or none
do — and returns a discriminated union you branch on with
`ts-pattern.match(...).exhaustive()`.

```ts
import { match } from "ts-pattern";

const result = await docStore.runTransaction({
  mutations: [
    { kind: "put", path: "todos/1.md", content: "buy milk\n", ifMatch: { kind: "any" } },
  ],
  message: "todo: add buy milk",
  onConflict: { kind: "rebase-mutations", maxAttempts: 3 },
});

match(result)
  .with({ kind: "ok" }, ({ commitOid }) => console.log("committed", commitOid))
  .with({ kind: "conflict" }, (e) => console.warn("conflict", e.paths))
  .with({ kind: "rejected" }, (e) => console.warn("validation", e.reason))
  .with({ kind: "network" }, (e) => console.warn("network", e.message))
  .exhaustive();
```

The `ifMatch` token implements optimistic concurrency. The `onConflict`
policy decides what happens when a remote changed since the last sync —
fail loud, retry after pull, or rebase the mutations onto the new HEAD.

## Choose your storage

`@dev-bench/git-fs` ships three adapters out of the box:

* `createBrowserGitFs` — LightningFS (IndexedDB) + isomorphic-git web
  http. Web app + Hermes (React Native).
* `createInMemoryGitFs` — pure-memory fs. Tests, sandboxes, fixtures.
* `createNodeGitFs` — node fs + isomorphic-git node http. CLI tools,
  servers.

And three management providers:

* `githubManagement` — GitHub REST.
* `giteaManagement` — Gitea REST.
* `gitlabManagement` — GitLab REST.

Pick whichever pair fits the host environment; the `DocStore` you build
on top is identical.

## Next

* [Getting Started](./getting-started.mdx) — wire it together end-to-end.
* [API](./api.mdx) — public exports, package by package.
