Data, owned
Your application's data should have an owner.
Fetching libraries dedupe requests. State libraries give you a store. Neither says which part of your application owns a piece of data, or what its status means to every consumer.
data-cap treats a piece of data, with the getters, mutators and subscriptions that touch it, as one capability-owned contract that every consumer reads the same synchronous snapshot from.
Give every piece of application data one owner and one shared status vocabulary, without replacing your database, transport or state library.
npm install data-cap
npx data-cap init
data-cap sits under or next to TanStack Query, Redux, Zustand or plain
fetch and useState. No database, transport or state-library
change is needed.
Why it exists
Fetching and state libraries answer different questions.
A fetching library dedupes requests. A state-management library gives you a store. Neither answers which piece of data a component owns versus merely consumes, whether an optimistic value will disagree with what other components expect, or which capability a subscription's transport is shared with.
Where the agreement lives today: each component's own idea of "loading", each component's own idea of "stale", each team's optimistic-update logic — no single source of truth for what the data's status means.
data-cap makes the agreement itself the artifact: a capability declares its fields and the operations that touch them, and every consumer reads the same synchronously available snapshot.
How it works
Full manual control, or batteries included: same primitives.
buildData, createDataStore and coordinator are
the same Stable primitives createData composes. Start wherever matches how
much control the capability needs.
The same Stable primitives can be composed manually for full control, or used through createData for a batteries-included dedup, status, and optimistic-mutation lifecycle.
Neither path is the one way: pick per capability. Fields are readable synchronously from
the moment buildData() or createData() returns, with no
throw-until-ready gate, and info is mandatory on every
DataState, so consumers never special-case whether metadata exists yet.
See it
Not a mockup: real output from this repository.
Run against examples/application in this repo:
Wrote evidence model: docs/data.evidence.json
[generate-docs] wrote src/generated/data.manifest.ts (1 active capability(ies)), docs/DATA.md, docs/OWNERSHIP.md, 4 flow file(s), and docs/data.evidence.json.
Source: examples/application
A manifest of every active capability, a documentation catalog, and a dependency and ownership report, statically derived from real code, not hand-maintained.
Guarantees
What is guaranteed, and where it stops.
Each claim states its scope and links to the document that defines it, so you can check the boundary before you rely on it.
Why not just TanStack Query, Redux, or manual fetch-and-state?
You keep all of them.
data-cap doesn't replace your transport or your store. It adds the layer none of them cover: one capability-owned contract that every consumer reads the same synchronous snapshot from, plus a static-analysis pass that turns it into ownership and dependency evidence.
| Tool | What it solves | What it doesn't |
|---|---|---|
Manual fetch + useState |
Gets data on screen | No shared status vocabulary, no dedup, no optimistic-update contract; every component re-derives its own version |
| TanStack Query | Fetching, caching, request dedup | Says nothing about mutation-driven data your app owns outside a query, or how two consumers agree on ownership |
| Redux / Zustand / a global store | One shared place to put state | Doesn't distinguish declared ownership from incidental access; anything can write anywhere |
Adoption
From manual control to batteries-included.
Most applications start with whichever primitive matches how much control a capability
needs: full manual control (buildData, createDataStore,
coordinator) or the batteries-included createData. Neither is
the one way.
One capability at a time
- Pick the primitive that fits one capability.
- Migrate the next capability when it needs more control or less boilerplate. Each is independent.
You probably don't need it when
Your application is small, one team owns the entire data layer, and there's little
reason to separate business capabilities from their storage or fetching
implementation. A conventional fetch-and-useState pattern, or your
existing state-management library alone, may be simpler.
For decision-makers
A one-page brief for your manager
data-cap gives every piece of application data one owner and one shared status, so every part of the application agrees on what the data is doing.
It answers what this is, why it matters, what it costs, what could go wrong and what is being asked, in plain language, on one printable page.
Proof, not promises
data-cap uses data-cap.
The project is self-hosted and covered by end-to-end examples and independently installed regression fixtures. The implementation, tests, architecture documentation and decision records are the evidence behind everything on this page.
Try it
Trial it on one feature.
Read the README for a first capability, or run an example to see the ownership report it produces.
data-cap is pre-1.0. Per VERSIONING.md, a minor release may still change a Stable API before 1.0, so pin accordingly and read the changelog on every minor upgrade.