Package Exports
- @dwk/store
Readme
@dwk/store
DO-SQLite quad store + R2 copy-on-write blob bodies behind one storage interface.
Part of the @dwk IndieWeb + Solid cohort. See the
package specification for the full requirements.
Runs inside the @dwk/solid-pod Durable Object and confines all Cloudflare
storage specifics, so the endpoint packages stay storage-agnostic and
unit-testable. Authoritative state lives only in DO SQLite and R2 — never KV.
What it does
- One interface, two backends. RDF triples live in the DO's SQLite quad
store; opaque bodies live in R2.
head,readQuads,writeQuads,patchQuads,putBlob,readBlob,putResource, anddeletehang off a singleStore. - Transactional writes.
writeQuads/patchQuadsapply an N3-Patch'sdeletes+insertsin onetransactionSync, and theIf-Match/If-None-Matchpreconditions are checked and applied inside the same transaction, so check-and-write is TOCTOU-free.deleteaccepts an optional in-transactionguardfor the same reason (e.g. LDP container emptiness). - Copy-on-write blobs.
putBlobwrites a new content-addressed R2 object, then atomically flips the DO pointer and outboxes any now-unreferenced predecessor key. Reads are streamed straight from R2 — never buffered. - Size-threshold routing.
putResourcekeeps small RDF in the quad store and offloads anything over the ~2 MB DO-cell ceiling to R2 as an opaque blob. - Sweep-free GC. Deletes drop the pointer first and record the orphaned key
to a transactional outbox in the same SQLite transaction.
forwardOrphansdrains the outbox into a shared D1 tracking store, andcollectGarbagereclaims R2 objects after a safety window using only D1 and R2 — it never scans or wakes a per-pod Durable Object. - Resurrection-safe reclaim. Because keys are content-addressed, a deleted
key can be revived by a later
putBlobof identical content.collectGarbagedeletes version-conditionally (re-checking the R2versionbetweenheadanddelete) andputBlobcancels an already-forwarded GC row, so GC never reclaims an object a live resource points at.
import { createStore } from "@dwk/store";
// Inside the solid-pod Durable Object:
const store = createStore(state, env);
const etag = await store.putBlob("/photo.jpg", bytes, {
contentType: "image/jpeg",
});
const body = await store.readBlob("/photo.jpg"); // { stream, etag, ... }