# Store

inseam's async, SQL-shaped persistence port. A host constructs a backend adapter and hands it to `openStore`; core wraps it with migrations, lifecycle, and typed errors. Plugins never touch the Store directly — typed APIs sit above it.

**See also:** [design](../design/store.md) · [spec](../spec/store.md)

## What it does

The Store is the seam between core and whichever SQL backend an instance ships with. Two surfaces, split by package:

- **Adapter surface** (`@inseam/store-contract`) — `StoreAdapter`, `Statement`, `RunResult`, `Migration`, plus `StoreError` / `AdapterError`. Zero runtime deps. Third parties compile against this.
- **Core surface** (`@inseam/core`) — `openStore`, `Store`, `MigrationError`, `BatchError`, `BindingError`, `StoreClosedError`. Adds the migration runner, batch serialization, and closed-state propagation on top of any adapter.

Atomicity is `batch` only — there are no interactive transactions. Migrations are core-owned, applied in ascending `id` order, and idempotent across re-opens. Concurrent `batch` calls on a `Store` are serialized in submission order.

## How to use it

A host wires an adapter into core:

```ts
import { openStore } from "@inseam/core";
import { bunSqliteAdapter } from "@inseam/adapter-store-bun-sqlite";

const store = await openStore(bunSqliteAdapter("./inseam.sqlite"));

const insert = store.prepare("INSERT INTO source (uri) VALUES (?)");
await store.batch([insert.bind("https://example/x"), insert.bind("https://example/y")]);

await store.close();
```

For a Cloudflare Worker host, swap in `@inseam/adapter-store-d1`. Both first-party adapters are documented together: [Bun-SQLite & D1 Store adapters](./bun-d1-sqlite-adapters.md).

Full type signatures live in the [spec](../spec/store.md#public-api).

## How to extend it

A new backend is a standalone package depending only on `@inseam/store-contract` and its driver. First-party adapters go under `pkgs/adapter-store-<backend>/`; third-party adapters publish as `inseam-adapter-store-<backend>`.

Implement `StoreAdapter`:

- `prepare(sql)` returns a reusable `Statement`. `bind(...)` produces a new bound statement; the original stays unbound.
- `batch(statements)` MUST be a real atomic unit (a SQLite transaction, `db.batch(...)` on D1).
- `health()` resolves `true`/`false`; never throws for connection-level failure.
- Wrap native driver errors in `AdapterError` with the original on `cause` before throwing.
- Do not import core or another adapter. Do not run migrations — core does that.

A sketch lives in the [spec's adapter-author section](../spec/store.md#adapter-author-perspective).

## Where the code lives

- Contract package: `pkgs/store-contract/src/index.ts`
- Conformance harness for adapter authors: `pkgs/store-contract/src/testing/run-store-adapter-tests.ts` (exported as `@inseam/store-contract/testing`)
- Core surface and Store wrapper: `pkgs/core/src/store/index.ts`
- Migration runner: `pkgs/core/src/store/migration-runner.ts`
- First-party adapters: `pkgs/adapter-store-bun-sqlite/`, `pkgs/adapter-store-d1/` — see [their doc](./bun-d1-sqlite-adapters.md).
- Test fake (consumed by core's Store tests, not exported): `pkgs/core/src/store/__fixtures__/fake-adapter.ts`

## Related

- [Spec — store](../spec/store.md) — mechanical contract and acceptance criteria
- [Design — store](../design/store.md) — rationale, two-adapter strategy, schema-ownership stance
- [Bun-SQLite & D1 Store adapters](./bun-d1-sqlite-adapters.md) — the two first-party adapters
- [Monorepo package model](./monorepo-package-model.md) — package roles the Store's packages instantiate
