Surfaced during #388 (Phase A of #386) Copilot review.
Problem
JSON-LD 1.1 supports JSON-literal values via @type: '@json' — used for embedding plain objects (like a JWK) in RDF as rdf:JSON datatype literals. JSS's Turtle conneg layer doesn't currently emit them.
src/rdf/turtle.js:valueToTerm (around line 350) only handles object values that are one of:
{ "@id": ... } — IRI reference
{ "@value": ..., "@language": ... } — plain literal
{ "@value": ..., "@type": ... } — typed literal
{ "@value": ... } — plain literal
A plain object value (e.g. a JWK {kty: "EC", crv: "P-256", x: "...", y: "..."}) returns null and is silently dropped.
Why it matters
#388's @context defines publicKeyJwk with @type: '@json' so a Phase B "add my keys" app can PATCH:
{
"verificationMethod": [{
"id": "...#passkey-1",
"type": "JsonWebKey",
"controller": "...#me",
"publicKeyJwk": { "kty": "EC", "crv": "P-256", "x": "...", "y": "..." }
}]
}
Without @type:'@json' handling in turtle.js, the JWK gets dropped on Turtle conneg — clients negotiating Turtle would see no key bytes, breaking auth.
Fix shape
Extend valueToTerm to handle the @json case. Two recognition paths:
- Context-driven: when the property's context entry says
@type: '@json', serialize the entire value (after JSON.stringify) as a single literal with datatype http://www.w3.org/1999/02/22-rdf-syntax-ns#JSON.
- Inline-typed value:
{ "@value": <object>, "@type": "@json" } — same output, value already typed inline.
Pseudocode addition in valueToTerm:
// @type:@json literal — JSON-LD 1.1 §4.2.2
if (isJsonType || (typeof value === 'object' && value['@type'] === '@json')) {
const json = JSON.stringify(value['@value'] !== undefined ? value['@value'] : value);
return literal(json, namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#JSON'));
}
Where isJsonType is plumbed in from the context lookup (similar to existing isIdType).
Acceptance
Refs
Surfaced during #388 (Phase A of #386) Copilot review.
Problem
JSON-LD 1.1 supports JSON-literal values via
@type: '@json'— used for embedding plain objects (like a JWK) in RDF asrdf:JSONdatatype literals. JSS's Turtle conneg layer doesn't currently emit them.src/rdf/turtle.js:valueToTerm(around line 350) only handles object values that are one of:{ "@id": ... }— IRI reference{ "@value": ..., "@language": ... }— plain literal{ "@value": ..., "@type": ... }— typed literal{ "@value": ... }— plain literalA plain object value (e.g. a JWK
{kty: "EC", crv: "P-256", x: "...", y: "..."}) returnsnulland is silently dropped.Why it matters
#388's @context defines
publicKeyJwkwith@type: '@json'so a Phase B "add my keys" app can PATCH:Without
@type:'@json'handling in turtle.js, the JWK gets dropped on Turtle conneg — clients negotiating Turtle would see no key bytes, breaking auth.Fix shape
Extend
valueToTermto handle the@jsoncase. Two recognition paths:@type: '@json', serialize the entire value (afterJSON.stringify) as a single literal with datatypehttp://www.w3.org/1999/02/22-rdf-syntax-ns#JSON.{ "@value": <object>, "@type": "@json" }— same output, value already typed inline.Pseudocode addition in
valueToTerm:Where
isJsonTypeis plumbed in from the context lookup (similar to existingisIdType).Acceptance
@context: { foo: { "@id": "ex:foo", "@type": "@json" } }and valuefoo: {a: 1, b: [2, 3]}round-trips to Turtle as a singlerdf:JSONliteral containing the canonical-ish JSON serializationbar: { "@value": {a: 1}, "@type": "@json" }(inline-typed form) also worksvalueToTermpaths (@id,@value+@language,@value+@type,@value) untouchedRefs