/** * Low-level document + SPARQL primitives. * * These call the real injected `ng` (`getConfig().ng`) directly — never the * public `ng` proxy (`makeNg`). This is a validated hard constraint, not a style * choice: the public `ng` is a JS `Proxy` over `@ng-org/web`'s iframe-RPC proxy, * and layering our Proxy on top breaks `doc_create`'s `postMessage` marshaling * with **`DataCloneError: function ... could not be cloned`** — the footgun this * rule exists to prevent. Reaching the real `ng` held in the config avoids the * double-proxy. Do not import from `./ng-proxy`. * * Signatures mirror the real `@ng-org/web` `ng` surface (verified against the * app's storeRegistry usage), so this is a drop-in for those raw calls. */ import { getConfig } from "./polyfill"; import { logAccess, enabled as accessLogEnabled } from "./access-log"; import type { Nuri } from "./types"; // The low common point for ALL document access: every read in the SDK routes // through `sparqlQuery`, every write through `sparqlUpdate` (+ container creation // through `docCreate`) — each ultimately calling the real injected `ng` here. The // access log is therefore instrumented HERE so no access path escapes it. Callers // pass a semantic `label` (readDoc|readUnion|listMyEntityDocs|writeEntity|deposit // |…); it is a lib-internal probe param, NOT forwarded to the real `ng` (the docs // primitives forward the exact SDK signature — see test/docs.test.ts). When the // log is OFF (default) the extra param is inert and costs one boolean read. /** Count rows in a raw SPARQL SELECT result, tolerant of the possible shapes. */ function rowCount(result: unknown): number { if (!result) return 0; if (Array.isArray(result)) return result.length; const anyRes = result as { results?: { bindings?: unknown[] } }; return anyRes.results?.bindings?.length ?? 0; } /** * Create one document → its NURI. * * Mirrors `ng.doc_create(session_id, crdt, cls, dest, store_repo?)`. For a graph * document in the (shared) private store: `docCreate(sid, "Graph", "data:graph", * "store")` (store_repo left undefined → private store). */ export async function docCreate( sessionId: string, crdt: string, cls: string, dest: string, store?: unknown, ): Promise { const { ng } = getConfig(); const nuri = await ng.doc_create(sessionId, crdt, cls, dest, store); // A container creation is a WRITE; the NURI only exists after the call. logAccess("WRITE", nuri, "docCreate"); return nuri; } /** * Run a SPARQL UPDATE (INSERT/DELETE DATA, etc.). * * Mirrors `ng.sparql_update(session_id, query, anchor?)`, where `anchor` is the * document NURI the update is scoped/base'd to (optional). */ export async function sparqlUpdate( sessionId: string, query: string, anchor?: Nuri, label = "sparqlUpdate", ): Promise { const { ng } = getConfig(); // `label` is a lib-internal access-log tag, NOT forwarded to `ng`. logAccess("WRITE", anchor ?? "(no anchor)", label); return ng.sparql_update(sessionId, query, anchor); } /** * Run a SPARQL SELECT/CONSTRUCT/ASK query → the raw SDK result. * * Mirrors `ng.sparql_query(session_id, query, base?, anchor?)`. `base` is the * query base IRI (usually `undefined`); `anchor` is the document NURI to query. */ export async function sparqlQuery( sessionId: string, query: string, base?: string, anchor?: Nuri, label = "sparqlQuery", ): Promise { const { ng } = getConfig(); // `label` is a lib-internal access-log tag, NOT forwarded to `ng`. const result = await ng.sparql_query(sessionId, query, base, anchor); // Log AFTER the read so the row count (a strong leak signal: a doc rendering // rows under an identity that should see nothing) can be appended. Skip the // rowCount work entirely when the log is off. if (accessLogEnabled()) { // `rows` here are raw RDF triple bindings (the SPARQL `?s ?p ?o` result), NOT // domain objects — one document's entity is spread across several triple rows. // Spell that out so the log isn't mistaken for an object count (the app-level // object/shape count is logged separately by useShapeQuery → dataStats). logAccess("READ", anchor ?? "(no anchor)", label, " → " + rowCount(result) + " triple-rows"); } return result; }