Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# jsonld ChangeLog

## Unreleased

### Fixed
- Copy the default base direction (`@direction`) when cloning the active
context, matching the existing handling of the default language. Previously
any context layer processed on top of an existing active context — a second
document-level layer, a property-/type-scoped context, an embedded node
`@context`, or a remote context — silently dropped the inherited
`@direction` (while `@language` survived), changing expanded output and,
under `rdfDirection`, the emitted N-Quads.
([#586](https://github.com/digitalbazaar/jsonld.js/issues/586))

## 9.0.0 - 2025-11-20

### Added
Expand Down
3 changes: 3 additions & 0 deletions lib/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -1432,6 +1432,9 @@ api.getInitialContext = options => {
if('@language' in this) {
child['@language'] = this['@language'];
}
if('@direction' in this) {
child['@direction'] = this['@direction'];
}
if('@vocab' in this) {
child['@vocab'] = this['@vocab'];
}
Expand Down
69 changes: 69 additions & 0 deletions tests/misc.js
Original file line number Diff line number Diff line change
Expand Up @@ -4374,3 +4374,72 @@ describe('safe canonize defaults', () => {
assert.deepStrictEqual(result, expected);
});
});

describe('@direction inheritance', () => {
// The default base direction is part of the active context and must
// survive cloning like the default language does (JSON-LD 1.1 API §4.1,
// Context Processing step 1). See issue #586.
it('should keep the default @direction across context layers', async () => {
const output = await jsonld.expand({
"@context": [
{"@language": "en", "@direction": "rtl"},
{"dummy": "http://example.com/dummy"}
],
"http://example.com/p": "v"
});
assert.deepStrictEqual(output, [{
"http://example.com/p": [
{"@language": "en", "@direction": "rtl", "@value": "v"}
]
}]);
});

it('should inherit the default @direction into a property-scoped context',
async () => {
const output = await jsonld.expand({
"@context": {
"@language": "en",
"@direction": "rtl",
"thing": {
"@id": "http://example.com/thing",
"@context": {"other": "http://example.com/other"}
}
},
"thing": {"http://example.com/label": "hello"}
});
assert.deepStrictEqual(output, [{
"http://example.com/thing": [{
"http://example.com/label": [
{"@language": "en", "@direction": "rtl", "@value": "hello"}
]
}]
}]);
});

it('should still allow a scoped context to override or clear @direction',
async () => {
const output = await jsonld.expand({
"@context": {
"@direction": "rtl",
"ltr": {
"@id": "http://example.com/ltr",
"@context": {"@direction": "ltr"}
},
"none": {
"@id": "http://example.com/none",
"@context": {"@direction": null}
}
},
"ltr": {"http://example.com/label": "a"},
"none": {"http://example.com/label": "b"}
});
assert.deepStrictEqual(output, [{
"http://example.com/ltr": [{
"http://example.com/label": [{"@direction": "ltr", "@value": "a"}]
}],
"http://example.com/none": [{
"http://example.com/label": [{"@value": "b"}]
}]
}]);
});
});
Loading