RDF/JS in JavaScript
In JavaScript, PurRDF does not invent its own API shape: the npm package
@blackcatinformatics/purrdf
implements the RDF/JS community specifications —
DataFactory, DatasetCore, and Stream/Sink — over the wasm-compiled
native engine. Code written against RDF/JS interfaces works with PurRDF terms
and datasets.
The data factory
DataFactory covers the standard RDF/JS term constructors — namedNode,
blankNode, literal(value, languageOrDatatype?), variable,
defaultGraph, quad, fromTerm, fromQuad — plus the deliberate RDF 1.2
extensions no incumbent RDF/JS library carries:
import { ready, DataFactory } from "@blackcatinformatics/purrdf";
await ready();
const f = new DataFactory();
// RDF/JS standard surface.
const q = f.quad(
f.namedNode("https://ex/alice"),
f.namedNode("https://ex/knows"),
f.namedNode("https://ex/bob"),
);
// RDF 1.2 extensions: quoted triple terms and base-direction literals.
const quoted = f.quotedTriple(q.subject, q.predicate, q.object);
const rtl = f.directionalLiteral("مرحبا", "ar", "rtl");
typedLiteral is a convenience alongside the spec’s overloaded literal.
Datasets
Dataset implements RDF/JS DatasetCore: add, delete, has, match,
size, and iteration (for (const quad of dataset)), plus parsing and
serialization through the native codecs:
const ds = Dataset.parse("<https://ex/s> <https://ex/p> <https://ex/o> .", "ntriples");
for (const quad of ds.match(null, f.namedNode("https://ex/p"), null)) {
console.log(quad.subject.value);
}
const trig = ds.serialize("trig");
Accepted format names: turtle, ntriples, nquads, trig, rdfxml, or
their media types. Because these are the native codecs, output is
byte-deterministic and identical to what the Rust, Python, and C surfaces
emit (Codecs & Determinism).
SPARQL
Use QueryEngine when running more than one query or when the caller wants
typed results instead of raw strings. The engine owns the native SPARQL plan
cache and returns package-root terms and datasets:
import { QueryEngine } from "@blackcatinformatics/purrdf";
const engine = new QueryEngine();
const result = engine.select(
ds,
"PREFIX ex: <https://ex/> SELECT ?o WHERE { ex:s ex:p ?o }",
);
console.log(result.rows.take(0)?.o?.value);
const graph = engine.construct(
ds,
"PREFIX ex: <https://ex/> CONSTRUCT { ex:copy ex:p ?o } WHERE { ex:s ex:p ?o }",
);
SELECT rows are single-owner and lazy across the wasm boundary. Iterate
result.rows, call result.rows.take(index) for indexed consumption, or call
result.rows.toArray() to materialize the remaining rows. A row can be consumed
once; call result.free() when abandoning a result before exhaustion.
QueryEngine.queryRaw(...) serializes SELECT/ASK results as SPARQL Results
JSON/XML/CSV/TSV and graph results through the same graph formats accepted by
Dataset.serialize. QueryEngine.update(...) applies SPARQL UPDATE atomically:
the dataset changes only after the whole update succeeds.
Streams and sinks
The package speaks the async RDF/JS Stream/Sink protocol over the
purrdf-events ingestion seam:
Sink— a streaming consumer:push(quad)per quad,finish()returns the accumulatedDataset.datasetToStream(dataset)/streamToDataset(stream)— bridge between aDatasetand RDF/JS streams, for piping into or out of other RDF/JS tooling.
Scope notes
- The engine is in-memory; there is no persistent store in the wasm
build. SPARQL runs over the in-memory dataset; this package provides no
network resolver, so remote
SERVICEandLOADfail explicitly. - A quoted-triple term as a quad object currently round-trips only through N-Quads (a current native serializer limitation for the other formats).
Related
- Getting Started: JavaScript / WebAssembly
- RDF 1.2 Features — what the extensions mean.
crates/rdf-wasm— the Rust cdylib behind the package.