You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The active-context clone used by context processing copies @base, @language, and @vocab, but not @direction. As a result the default base direction is silently dropped whenever a new context layer is processed on top of an existing active context — a second document-level @context layer, a property-scoped context, a type-scoped context, an embedded node @context, or a remote context. The default language is inherited correctly in all of these cases; only @direction is lost.
Reproduced on jsonld.js 9.0.0; the omission is still present in _cloneActiveContext on current main.
Reproduction
importjsonldfrom'jsonld';// 1. Control: a single context layer sets both defaults — both apply.console.log(JSON.stringify(awaitjsonld.expand({'@context': {'@language': 'en','@direction': 'rtl'},'http://ex/p': 'v'})));// 2. A second document-level context layer: @language survives, @direction is lost.console.log(JSON.stringify(awaitjsonld.expand({'@context': [{'@language': 'en','@direction': 'rtl'},{'dummy': 'http://ex/d'}],'http://ex/p': 'v'})));// 3. A property-scoped context: @language is inherited into the scope, @direction is lost.console.log(JSON.stringify(awaitjsonld.expand({'@context': {'@language': 'en','@direction': 'rtl','thing': {'@id': 'http://ex/thing','@context': {'other': 'http://ex/other'}}},'thing': {'http://ex/label': 'hello'}})));// 4. toRDF with rdfDirection (jsonld-signatures' default): the divergence reaches N-Quads.console.log(awaitjsonld.toRDF({'@context': {'@language': 'en','@direction': 'rtl','thing': {'@id': 'http://ex/thing','@context': {'other': 'http://ex/other'}}},'@id': 'http://ex/x','http://ex/out': 'outside','thing': {'http://ex/label': 'hello'}},{format: 'application/n-quads',rdfDirection: 'i18n-datatype'}));
Expected (per spec): case 2 keeps "@direction":"rtl"; case 3's hello carries "@direction":"rtl"; case 4 emits "hello"^^<https://www.w3.org/ns/i18n#en_rtl> for the in-scope literal. Note case 4: within one document, the top-level string gets the i18n datatype while the string inside the scope silently degrades to a plain language-tagged literal.
@direction is never copied. Context processing starts each layer with rval = rval.clone(), so any layer processed after the one that set @direction loses it (unless that layer restates it — explicitly setting@direction works, because the '@direction' in ctx handler assigns onto the clone; only inheritance is broken). The clone predates JSON-LD 1.1's base-direction support and appears never to have been updated.
Spec reference
JSON-LD 1.1 API, Context Processing: the active context "consists of: the active term definitions …, the current base IRI, an inverse context, an optional vocabulary mapping, an optional default language, an optional default base direction, and an optional previous context", and step 1 of the algorithm is "Initialize result to the result of cloning active context, with inverse context set to null." Step 5.8 then only modifies the default base direction "If context has a @direction entry" — an absent entry leaves the inherited value in place.
A one-line fix in _cloneActiveContext appears sufficient:
PyLD shares the identical omission — _clone_active_context in lib/pyld/jsonld.py copies mappings, @base, previousContext, @language, @vocab and not @direction — so a fix here should probably be mirrored there.
Implementations diverge on this today: Titanium JSON-LD (Java) copies both defaultLanguage and defaultBaseDirection in its ActiveContext copy constructor, so it inherits @direction per spec and produces different expansion/N-Quads output than jsonld.js for the documents above.
The W3C test suite doesn't cover this — there is no json-ld-api fixture combining a default @direction with a subsequent context layer or scoped context, which is presumably why it has gone unnoticed. A suite test may be worth proposing alongside a fix.
Summary
The active-context clone used by context processing copies
@base,@language, and@vocab, but not@direction. As a result the default base direction is silently dropped whenever a new context layer is processed on top of an existing active context — a second document-level@contextlayer, a property-scoped context, a type-scoped context, an embedded node@context, or a remote context. The default language is inherited correctly in all of these cases; only@directionis lost.Reproduced on jsonld.js 9.0.0; the omission is still present in
_cloneActiveContexton currentmain.Reproduction
Actual output (jsonld.js 9.0.0):
Expected (per spec): case 2 keeps
"@direction":"rtl"; case 3'shellocarries"@direction":"rtl"; case 4 emits"hello"^^<https://www.w3.org/ns/i18n#en_rtl>for the in-scope literal. Note case 4: within one document, the top-level string gets the i18n datatype while the string inside the scope silently degrades to a plain language-tagged literal.Root cause
_cloneActiveContextinlib/context.js:@directionis never copied. Context processing starts each layer withrval = rval.clone(), so any layer processed after the one that set@directionloses it (unless that layer restates it — explicitly setting@directionworks, because the'@direction' in ctxhandler assigns onto the clone; only inheritance is broken). The clone predates JSON-LD 1.1's base-direction support and appears never to have been updated.Spec reference
JSON-LD 1.1 API, Context Processing: the active context "consists of: the active term definitions …, the current base IRI, an inverse context, an optional vocabulary mapping, an optional default language, an optional default base direction, and an optional previous context", and step 1 of the algorithm is "Initialize result to the result of cloning active context, with inverse context set to
null." Step 5.8 then only modifies the default base direction "If context has a@directionentry" — an absent entry leaves the inherited value in place.A one-line fix in
_cloneActiveContextappears sufficient:Notes
_clone_active_contextinlib/pyld/jsonld.pycopiesmappings,@base,previousContext,@language,@vocaband not@direction— so a fix here should probably be mirrored there.defaultLanguageanddefaultBaseDirectionin itsActiveContextcopy constructor, so it inherits@directionper spec and produces different expansion/N-Quads output than jsonld.js for the documents above.@directionwith a subsequent context layer or scoped context, which is presumably why it has gone unnoticed. A suite test may be worth proposing alongside a fix.jsonld-signaturesdefaultsrdfDirectiontoi18n-datatype, fixing this changes canonical N-Quads (and therefore RDFC hashes / proof values) for any signed document that combines a default@directionwith multiple context layers or scoped contexts. Related standards discussion on@direction/rdfDirectiondivergence in Data Integrity: RDFC-based cryptosuites do not specifyrdfDirection; implementations already diverge on documents with@directionw3c/vc-data-integrity#366.