npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@oxilite/d1

v0.7.0

Published

oxilite on Cloudflare D1 and Durable Objects: an Oxigraph-compatible SPARQL 1.1 and openCypher store (WebAssembly core)

Readme

@oxilite/d1

npm license

An Oxigraph-compatible SPARQL 1.1 and openCypher store on Cloudflare D1 and Durable Objects. The oxilite core runs as WebAssembly: it compiles each query to SQL for your env.DB binding, so a query is one D1 call and an update is one atomic batch. No Rust toolchain needed.

Website · npm · Guide and architecture · Changelog and issues

npm install @oxilite/d1

1. Create the schema

npx wrangler d1 create my-graph
npx oxilite-d1 schema > migrations/0001_oxilite.sql          # --no-graph-index to save writes
                                                             # --jsonld for JSON-LD / credential tables
npx wrangler d1 migrations apply my-graph --remote

Upgrading to 0.3.0 from 0.2.x: 0.3.0 adds the schema_graphs, shapes_index, shapes_in and datalog_work tables. A D1 store is opened with open_existing, so no DDL runs per request and an existing database will report no such table: schema_graphs until you apply a new migration. Regenerate and apply it:

npx wrangler d1 migrations create my-graph oxilite-0-3-0
npx oxilite-d1 schema > migrations/0002_oxilite-0-3-0.sql
npx wrangler d1 migrations apply my-graph --remote

Every statement is CREATE TABLE IF NOT EXISTS, so re-applying it is safe and your data is untouched.

2. Query it from a Worker

import { D1Store } from "@oxilite/d1";
import wasm from "@oxilite/d1/oxilite.wasm";

export default {
  async fetch(req: Request, env: { DB: D1Database }): Promise<Response> {
    const store = await D1Store.open(env.DB, { wasm, migrated: true });
    const url = new URL(req.url);

    if (req.method === "POST" && url.pathname === "/update") {
      await store.update(await req.text());               // one atomic D1 batch
      return new Response(null, { status: 204 });
    }
    const q = url.searchParams.get("query") ?? "SELECT * WHERE { ?s ?p ?o } LIMIT 10";
    return new Response(await store.queryJson(q), {
      headers: { "content-type": "application/sparql-results+json" },
    });
  },
};

Cypher on D1

await store.cypher(
  "UNWIND $rows AS row MERGE (p:Person {id: row.id}) SET p.name = row.name",
  { rows: [{ id: 1, name: "Ada" }, { id: 2, name: "Alan" }] },
  { base: "https://example.com/" },
);
const r = await store.cypher(
  "MATCH p = shortestPath((a:Person {id: 1})-[:KNOWS*]-(b:Person {id: 2})) RETURN length(p) AS hops",
  {}, { base: "https://example.com/" },
);

Every Cypher write is one atomic batch; shortestPath is a breadth-first search with one request per level. SPARQL and Cypher see the same data.

JSON-LD documents and Verifiable Credentials on D1

const vcs = store.credentials();                     // W3C credential contexts are bundled
const id = await vcs.put(credentialJson);            // one D1 batch: JSON row + RDF in graph <id>
await vcs.putPresentation(presentationJson);         // embedded credentials stored too, same batch
const valid = await vcs.find({ issuer: "did:example:issuer", validAt: new Date() });
const raw = (await vcs.get(id))?.json;               // the exact bytes

const docs = store.jsonld();                         // any JSON-LD document
await docs.putContext("https://example.org/my-context", myContext);   // persisted in D1
await docs.put(documentJson);

Contexts load offline: bundled credential contexts, contexts passed in options.contexts, and contexts persisted in D1 with putContext. A Worker never fetches contexts. Every write is one batch, and a document too large for one batch fails with document-too-large rather than being split. Pass migrated: true to jsonld() / credentials() when your migration already created the tables (oxilite-d1 schema --jsonld). JSON-LD and credential support adds about 1.7 MB (0.4 MB gzipped) to the wasm module, which stays well within the Workers size limits.

Durable Objects

The same store runs on a Durable Object's embedded SQLite through a small adapter over ctx.storage.sql, which gives every agent or user a private graph. examples/do-agent-memory-ts is a tested agent-memory Worker built this way.

API

D1Store mirrors Oxigraph's JavaScript Store, asynchronously: query, queryJson, update, load, bulkLoad, dump, add, delete, has, match, size, clear, plus cypher, explain, explainUpdate, explainCypher, optimize, materialize (OWL 2 RL as SQL rules), clearInferences, jsonld(options) and credentials(options). The schema registry is there too: registerSchemaGraph(graph, role, { appliesTo }), schemaGraphs(), setSchemaGraphActive, unregisterSchemaGraph, dropSchemaGraph, shapeIndex(); registrations are RDF in <oxilite:schema>, written with the same portable SPARQL as on Oxigraph. D1Store.open(db, { systemGraphs: true }) (or npx oxilite-d1 schema --system-graphs) starts a blank database with the oxl: vocabulary in <oxilite:vocabulary>; installSystemGraphs() adds it to an existing one. Query options add reasoning: "rdfs" | "owl-ql", include_inferred and include_schema_graphs; create the store with textIndex: true for FTS5 search with oxl:textMatch.

| Import | Use | |---|---| | @oxilite/d1 | Workers: pass the wasm module (@oxilite/d1/oxilite.wasm) to D1Store.open | | @oxilite/d1/node | Node.js, tests and Miniflare: the wasm core loads itself | | npx oxilite-d1 schema [--jsonld] [--versioning log] | Print the schema as a D1 migration (with the JSON-LD tables, with versioning) | | npx oxilite-d1 versioning-migration --from off --to log | Print the migration that changes an existing database's versioning level |

Versioning

Open or create the store with versioning: "stamped" (a store clock) or "log" (an immutable change log). Every D1 batch is then one commit:

const store = await D1Store.open(env.DB, { wasm, versioning: "log" });
await store.withCommit({ author: "ada", message: "close t1" }, (s) => s.update(closeTicket));
const before = await store.query(sparql, { as_of: "HEAD~1" });     // or "#42", "@2026-09-01T12:00:00Z"
const diff = await store.diff("HEAD~1");                            // [{ tick, added, quad }]
const log = await store.history(20);                                // [{ tick, time, kind, author, message, added, removed }]

versioning(), setVersioning(level, { allowLoss }), setCommitInfo, changes(after), resolveVersion and purge(pattern, reason) complete the API. SERVICE <oxilite:version/HEAD~1> { … } compares versions inside one query, GRAPH <oxilite:history> reads commits and changes as RDF, and cypher(q, {}, { asOf: "HEAD~1" }) matches the past. Measured on D1: stamped writes 4.82 rows per triple against 4.81 for a plain store, log 6.82 and log with asOfIndex 8.82. A store keeps its level: change an existing database with a migration.

Upgrading a 0.4 database

0.5 moves the schema registry into the RDF graph <oxilite:schema> and adds a scope column to the reasoning cache. D1Store.open(env.DB, { wasm }) (without migrated: true) upgrades a 0.4 database in one batch: registrations become triples, the schema_graphs table goes, and the cache is rebuilt. Do it once (for example from a one-off script or a deploy step), then keep opening with migrated: true; openExisting on a database that was not upgraded fails with a message saying so. New databases: regenerate migrations/0001_oxilite.sql with npx oxilite-d1 schema.

Tips

  • Run store.optimize() after large imports, not on every request: it refreshes planner statistics.
  • Drop the graph index (--no-graph-index, graphIndex: false) if you only use the default graph: one fewer index write per triple.
  • Use explain() in development to check a query compiles fully to SQL. Queries that need the Rust fallback evaluator report "unsupported" on D1.

More in the D1 guide. A complete Worker with its migration and a Miniflare test is in examples/d1-worker-ts.

The oxilite family

oxilite is an Oxigraph-compatible RDF database and SPARQL 1.1 engine that stores its data in SQLite, so it runs anywhere SQLite runs: in-process, on a system or vendor libsqlite3, on Cloudflare D1 and in Durable Objects. The same data can be queried with SPARQL and openCypher, reasoned over with RDFS / OWL, and validated with SHACL and ShEx. Read the overview on oxilitedb.com and the full guide in the main README.

| Package | What it is for | |---|---| | oxilite | The store: a drop-in for oxigraph::store::Store, plus AsyncStore for D1 | | oxilite-core | The sans-IO core: term encoding, schema, SPARQL → SQL compiler and planner | | oxilite-rusqlite | In-process backend with a bundled SQLite (the default) | | oxilite-dylib | Backend that loads your own libsqlite3 at runtime | | oxilite-d1 | Cloudflare D1 backend for Rust Workers | | oxilite-cypher | openCypher over the same data, OWL- and SHACL-aware | | oxilite-jsonld | JSON-LD documents stored verbatim, one named graph each | | oxilite-vc | Verifiable Credentials: stored under their id, indexed, queryable | | oxilite-reason | OWL 2 RL materialization with reasonable | | oxilite-validate | SHACL and ShEx validation with rudof | | oxilite-cli | The oxilite command and a SPARQL endpoint like oxigraph serve | | @oxilite/node | Node.js bindings, API of Oxigraph's JS package | | @oxilite/d1 | Cloudflare D1 and Durable Objects from TypeScript (WebAssembly core) | | @oxilite/common | RDF/JS terms and shared TypeScript types |

License

Dual-licensed under MIT or Apache-2.0, at your option, like Oxigraph.