Live mode — this page may load external map tiles and query remote endpoints; the standard hub is fully self-contained (same cells, no network).

Every other page in this hub loads @factoidal/core the same way the rest of this site does: same-origin, from docs/npm/factoidal/, built by this repo's own CI. That proves the engine works. It does not prove that what a stranger gets from npm install @factoidal/core is the same thing — a publish step, a stale files list, or a CDN mirror serving the wrong layout could all break that link silently.

This page closes that gap. It loads @factoidal/core@0.1.0 from the public npm registry, via a CDN that serves npm packages verbatim, and runs it side by side with the same-origin copy.

This page needs network access. Every other hub page works completely offline (same-origin only, by CSP). This one's whole point is the opposite: it fetches real code from cdn.jsdelivr.net / unpkg.com, so this post's Content-Security-Policy allows exactly those two hosts and nothing else external. No other post carries the carve-out. If both CDNs are unreachable, every cell reports "registry unreachable" instead of failing outright.

Loading the published package#

registry = {
  try {
    return await fn.loadRegistryPackage("0.1.0");
  } catch (err) {
    return { unavailable: `could not load @factoidal/core@0.1.0 from a CDN: ${err.message}` };
  }
}

Two versions, side by side#

The version this page is running (same-origin, built by this repo's CI for this deploy) and the version the registry served can differ — that is the point of this page, not a bug to fix. A reader comparing them sees exactly what "installed" vs. "vendored" means in practice.

return {
  siteBundleVersion: Factoidal.version,
  registryPackageVersion: registry.unavailable ? null : registry.version,
  registryStatus: registry.unavailable ? registry.unavailable : "loaded from the npm registry CDN",
};

A small dataset, parsed by the registry module#

DATA_TTL = `
  @prefix : <http://example.org/> .
  :alice :name "Alice" ; :age 30 .
  :bob   :name "Bob"   ; :age 7 .
`
registryDataset = {
  if (registry.unavailable) return { unavailable: registry.unavailable };
  return await registry.parse(DATA_TTL);
}

SELECT, through the registry module#

if (registry.unavailable) return { unavailable: registry.unavailable };
const rows = await registry.query(registryDataset, `
  # Every person and their age, sorted by subject.
  PREFIX : <http://example.org/>
  SELECT ?s ?age WHERE { ?s :age ?age } ORDER BY ?s`);
return pretty(rows);

ASK, through the registry module#

if (registry.unavailable) return { unavailable: registry.unavailable };
return await registry.query(registryDataset, `
  # Is anyone in the dataset 18 or older?
  PREFIX : <http://example.org/>
  ASK { ?s :age ?age . FILTER(?age >= 18) }`);

RDFC-1.0 canonicalize, through the registry module#

if (registry.unavailable) return { unavailable: registry.unavailable };
const canon = await registry.canonicalize(DATA_TTL, { format: "turtle" });
return canon;

An extension function, registered on the registry module#

The same SPARQL 1.1 §17.6 seam from post 34, this time registered against the code fetched from the registry, not this site's own copy:

if (registry.unavailable) return { unavailable: registry.unavailable };
await registry.registerExtensionFunction(
  "http://example.org/fn#isAdult",
  ([age]) => Number(age.value) >= 18
);
const rows = await registry.query(registryDataset, `
  # Everyone the custom fn:isAdult extension function accepts.
  PREFIX : <http://example.org/>
  PREFIX fn: <http://example.org/fn#>
  SELECT ?s WHERE { ?s :age ?age . FILTER(fn:isAdult(?age)) }`);
return pretty(rows);

Cross-check: same-origin and registry agree#

Same query, same data, two independently-loaded copies of the engine — one same-origin, one fetched fresh from the registry. Their answers should be identical.

if (registry.unavailable) return { unavailable: registry.unavailable };
const q = `
  # Every person and their age, sorted by subject -- run through both
  # engine copies so their answers can be compared.
  PREFIX : <http://example.org/>
  SELECT ?s ?age WHERE { ?s :age ?age } ORDER BY ?s`;
const siteDataset = await fn.parse(DATA_TTL);
const siteRows = await fn.query(siteDataset, q);
const registryRows = await registry.query(registryDataset, q);
const norm = (rows) => rows.map((r) => ({ s: r.get("s").value, age: r.get("age").value }));
const a = norm(siteRows);
const b = norm(registryRows);
return { agree: JSON.stringify(a) === JSON.stringify(b), siteRows: a, registryRows: b };

What this actually checks — and what it does not#

Post 34 is the extension-function seam this page reuses. Post 16 is the verification story this page's provenance check is downstream of.