Repository navigation
fix(client,site,gui): model RDF 1.2 triple terms; preload hashed engine wasm on /app - #6661
Conversation
…erm writer (#6044) The engine emits SPARQL 1.2 JSON {"type":"triple","value":{subject,predicate,object}} for an RDF 1.2 triple term, but SparqlTerm declared type uri|literal|bnode with a string value. site/src/lib/repl-dataset.ts's termToNT switch had no triple case, so storeToNQuads wrote the string "undefined" into the REPL dataset snapshot. - SparqlTerm is now SparqlAtomicTerm | SparqlTripleTerm (discriminated on type). - New shared helpers in @sparq/client: termToNTriples (the one N-Triples/N-Quads term writer, <<( s p o )>> for triple terms, throws on an unknown kind), termValue, isTripleTerm; formatTerm and CSV/TSV export handle triple terms. - The site REPL snapshot writer and the GUI termToNT delegate to termToNTriples; every call site that read term.value as a string now narrows or uses termValue. - Regression tests: site repl-dataset (red before, green after), client sparql-term suite, GUI inferred-facts key round-trip with a triple term. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ScyGGohDhirnbLbrUSnA5n
…requests (#2422) The /app layout preloaded /wasm/sparq_wasm_bg.wasm and /wasm/sparq_wasm.js, but the @sparq/client loader resolves the engine through public/wasm/wasm-manifest.json to the content-hashed copies. On the hosted build the preload never matched, so the browser downloaded the engine wasm twice (unused preload + hashed copy). Resolve the hints at build time through the same resolveWasmAssetWithManifest the loader uses, so the URLs are identical by construction (falls back to the unhashed names when no manifest exists, e.g. dev). Verified with build:web: out/index.html now preloads /app/wasm/sparq_wasm-<hash>.js and /app/wasm/sparq_wasm_bg-<hash>.wasm. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ScyGGohDhirnbLbrUSnA5n
🔎 Codex reviewer —
|
Inferred-fact identity keyed RDF 1.2 triple terms by their N-Triples spelling. The shared JS writer emits a raw TAB inside a literal while the Rust closure writer emits `\t`, so an asserted triple-term fact was reported as inferred and SPARQL binding keys missed the closure keys. Both key functions now build triple-term keys recursively from the component keys (term type, value, datatype, language, direction), and the RDF 1.2 base direction is a literal key component on both paths. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ScyGGohDhirnbLbrUSnA5n
Conflict in gui/app/package.json test:unit: kept both sides (main's import-url and workspace-sources tests plus this branch's wasm-preload test). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ScyGGohDhirnbLbrUSnA5n
🔎 Codex reviewer —
|
This PR made site/src/lib/repl-dataset.ts and result-graph.ts import runtime values (termToNTriples, formatTerm) from @sparq/client. That specifier is only an alias in tsconfig paths and next.config, so Node failed with ERR_MODULE_NOT_FOUND and three test files (rdf-import, repl-dataset, result-graph) did not load. Map the bare specifier to the same source entry in the test loader. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ScyGGohDhirnbLbrUSnA5n
…d keys
termToNTriples and formatTerm wrote a directional literal such as
{"xml:lang":"en","its:dir":"ltr"} as "hi"@en, both at the top level and
inside a triple term, so site/GUI dataset snapshots changed the term's
identity and literals differing only by direction collapsed. Add its:dir
to SparqlAtomicTerm and emit it as @lang--dir. The site and GUI graph-view
node keys now include the direction too.
Tests: client writer/formatter (top-level, nested, parse round trip), site
rowsToNQuads round trip, GUI termToNT key round trip, and graph-view node
identity in both site and GUI.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ScyGGohDhirnbLbrUSnA5n
737b108 to
763fccf
Compare
🔎 Codex reviewer —
|
# Conflicts: # site/test-support/ts-loader.mjs
🔎 Codex reviewer —
|
|
Local ci-fast gate (GitHub Actions outage; Jesse approved local-gate merges). PR head
Squash-merging under the local-gate rule. Generated by Claude Code |
Requested by Jesse · project thread
Summary
Two frontend fixes (JS/TS only, no Rust).
#6044 — RDF 1.2 triple terms in
SparqlTerm. The engine emits SPARQL 1.2 JSON{"type":"triple","value":{"subject":…,"predicate":…,"object":…}}(crates/sparq-engine/src/json.rs), butSparqlTermdeclaredtype: "uri"|"literal"|"bnode"with a stringvalue. The site REPL'srepl-dataset.ts::termToNThad no triple case, sostoreToNQuadswrote the stringundefinedinto the dataset snapshot. (PR #5778, which the issue cites as fixing the GUI writer, was closed unmerged, so the GUI writer had the same gap.)SparqlTermis now a discriminated unionSparqlAtomicTerm | SparqlTripleTerm. This is a breaking type change for@sparq/client(a private workspace package): readingterm.valueas a string now requires narrowing ontype.@sparq/client:termToNTriples(the single N-Triples/N-Quads term writer; writes<<( s p o )>>, recurses into nested terms, throws on an unknown kind instead of emittingundefined),termValue, andisTripleTerm.formatTermand CSV/TSV export handle triple terms.termToNTboth delegate totermToNTriples. Every call site that readterm.valueas a string (site REPL cells/graph, SHACL playground, VC and Solid demos, GUI ODRL tool, SELECT graph, engine-context graph stats) now narrows or usestermValue. The SHACL Compact serialiser throws a clear error for a triple term because SCS cannot express one.\t. The shared writer matches the engine serialiser and leaves it raw, which is still valid N-Triples. Inferred-fact identity compares decoded keys, so it is unaffected.#2422 — /app downloads the engine wasm twice. The GUI layout preloaded the unhashed
/wasm/sparq_wasm_bg.wasmand/wasm/sparq_wasm.js. The runtime loader resolves the engine throughpublic/wasm/wasm-manifest.jsonto the content-hashed copies (sq-b66fc). On the hosted build the preload never matched, so the browser fetched the multi-MB engine twice. The hints are now resolved at build time with the sameresolveWasmAssetWithManifestthe loader uses (newgui/app/src/lib/wasm-preload.ts), so the two URLs match by construction. If there is no manifest (dev), both fall back to the unhashed names. I verified it withnpm run build:web(stub wasm):out/index.htmlnow hasmodulepreload /app/wasm/sparq_wasm-<hash>.jsandpreload /app/wasm/sparq_wasm_bg-<hash>.wasm. I could not load the live site from this sandbox (the proxy refuses it), so the issue's third download is not reproduced. The tier-b bundles (reason/text/rsp) already load lazily on tool open, and nothing else fetches wasm at mount. If a third copy still shows after deploy, it needs a live network trace.No new frontend dependencies (AGENTS.md's optional-code policy):
wasm-preload.tsis server-only and runs at build time.Closes #6044
Closes #2422
Base gate (always required)
cargo build --workspacesucceeds. (No Rust changes; left to CI.)cargo clippy --workspace --exclude sparq-py --all-targets -- -D warningsis clean. (No Rust changes; left to CI.)packages/sparq-clientnpm test(18 pass, including the newsparql-term.test.mjs),sitenpm run test:unit(403 pass; the new repl-dataset triple-term test failed before the fix and passes after),gui/appnpm run test:unit(126 pass, including the newwasm-preload.test.tsand a triple-term case ininferred-facts.test.ts).tsc --noEmitis clean in all three, andnext lintis clean on the touched site/GUI files.Targeted re-evaluation (check the rows that apply to your change)
pubitem / CLI flag / HTTP route / Py/JS binding):@sparq/clientis a private workspace package with noskills/surface doc, so there was no SKILL.md to update. Its tests were re-run.Ratchets and conventions
TODO/FIXMEmarkers added.Security
🤖 Generated with Claude Code
https://claude.ai/code/session_01ScyGGohDhirnbLbrUSnA5n
Generated by Claude Code