@@ -42,17 +42,16 @@ api.cache = new ActiveContextCache();
42
42
* @param activeCtx the current active context.
43
43
* @param localCtx the local context to process.
44
44
* @param options the context processing options.
45
- * @param isPropertyTermScopedContext `true` if `localCtx` is a scoped context
46
- * from a property term.
47
- * @param isTypeScopedContext `true` if `localCtx` is a scoped context
48
- * from a type.
45
+ * @param propagate `true` if `false`, retains any previously defined term,
46
+ * which can be rolled back when the descending into a new node object changes.
47
+ * @param overrideProtected `false` allows protected terms to be modified.
49
48
*
50
49
* @return a Promise that resolves to the new active context.
51
50
*/
52
51
api . process = async ( {
53
52
activeCtx, localCtx, options,
54
- isPropertyTermScopedContext = false ,
55
- isTypeScopedContext = false
53
+ propagate = true ,
54
+ overrideProtected = false ,
56
55
} ) => {
57
56
// normalize local context to an array of @context objects
58
57
if ( _isObject ( localCtx ) && '@context' in localCtx &&
@@ -66,27 +65,22 @@ api.process = async ({
66
65
return activeCtx ;
67
66
}
68
67
69
- // track the previous context
70
- const previousContext = activeCtx . previousContext || activeCtx ;
71
-
72
- // if context is property scoped and there's a previous context, amend it,
73
- // not the current one
74
- if ( isPropertyTermScopedContext && activeCtx . previousContext ) {
75
- // TODO: consider optimizing to a shallow copy
76
- activeCtx = activeCtx . clone ( ) ;
77
- activeCtx . isPropertyTermScoped = true ;
78
- activeCtx . previousContext = await api . process ( {
79
- activeCtx : activeCtx . previousContext ,
80
- localCtx : ctxs ,
81
- options,
82
- isPropertyTermScopedContext
83
- } ) ;
84
- return activeCtx ;
68
+ // override propagate if localCtx has `@propagate`
69
+ if ( _isObject ( ctxs [ 0 ] ) && '@propagate' in ctxs [ 0 ] ) {
70
+ // Retrieve early, error checking done later
71
+ propagate = ctxs [ 0 ] [ '@propagate' ] ;
85
72
}
86
73
87
74
// process each context in order, update active context
88
75
// on each iteration to ensure proper caching
89
76
let rval = activeCtx ;
77
+
78
+ // track the previous context
79
+ // If not propagating, make sure rval has a previous context
80
+ if ( ! propagate && ! rval . previousContext ) {
81
+ rval . previousContext = activeCtx . clone ( ) ;
82
+ }
83
+
90
84
for ( let i = 0 ; i < ctxs . length ; ++ i ) {
91
85
let ctx = ctxs [ i ] ;
92
86
@@ -97,7 +91,7 @@ api.process = async ({
97
91
if ( ctx === null ) {
98
92
// We can't nullify if there are protected terms and we're
99
93
// not processing a property term scoped context
100
- if ( ! isPropertyTermScopedContext &&
94
+ if ( ! overrideProtected &&
101
95
Object . keys ( activeCtx . protected ) . length !== 0 ) {
102
96
const protectedMode = ( options && options . protectedMode ) || 'error' ;
103
97
if ( protectedMode === 'error' ) {
@@ -134,10 +128,6 @@ api.process = async ({
134
128
{ code : 'invalid protected mode' , context : localCtx , protectedMode} ) ;
135
129
}
136
130
rval = activeCtx = api . getInitialContext ( options ) . clone ( ) ;
137
- // if context is type-scoped, ensure previous context has been set
138
- if ( isTypeScopedContext ) {
139
- rval . previousContext = previousContext . clone ( ) ;
140
- }
141
131
continue ;
142
132
}
143
133
@@ -256,6 +246,25 @@ api.process = async ({
256
246
defined . set ( '@language' , true ) ;
257
247
}
258
248
249
+ // handle @propagate
250
+ // note: we've already extracted it, here we just do error checking
251
+ if ( '@propagate' in ctx ) {
252
+ const value = ctx [ '@propagate' ] ;
253
+ if ( activeCtx . processingMode === 'json-ld-1.0' ) {
254
+ throw new JsonLdError (
255
+ 'Invalid JSON-LD syntax; @propagate not compatible with ' +
256
+ activeCtx . processingMode ,
257
+ 'jsonld.SyntaxError' ,
258
+ { code : 'invalid context member' , context : ctx } ) ;
259
+ }
260
+ if ( typeof value !== 'boolean' ) {
261
+ throw new JsonLdError (
262
+ 'Invalid JSON-LD syntax; @propagate must be boolean valued' ,
263
+ 'jsonld.SyntaxError' ,
264
+ { code : 'invalid @propagate value' , context : localCtx } ) ;
265
+ }
266
+ }
267
+
259
268
// handle @protected ; determine whether this sub-context is declaring
260
269
// all its terms to be "protected" (exceptions can be made on a
261
270
// per-definition basis)
@@ -294,12 +303,11 @@ api.process = async ({
294
303
* @param {string } [options.protectedMode="error"] - "error" to throw error
295
304
* on `@protected` constraint violation, "warn" to allow violations and
296
305
* signal a warning.
297
- * @param isPropertyTermScopedContext `true` if `localCtx` is a scoped context
298
- * from a property term.
306
+ * @param overrideProtected `false` allows protected terms to be modified.
299
307
*/
300
308
api . createTermDefinition = (
301
309
activeCtx , localCtx , term , defined , options ,
302
- isPropertyTermScopedContext = false ) => {
310
+ overrideProtected = false ) => {
303
311
if ( defined . has ( term ) ) {
304
312
// term already defined
305
313
if ( defined . get ( term ) ) {
@@ -699,9 +707,8 @@ api.createTermDefinition = (
699
707
'jsonld.SyntaxError' , { code : 'invalid keyword alias' , context : localCtx } ) ;
700
708
}
701
709
702
- // FIXME if(1.1) ... ?
703
- if ( previousMapping && previousMapping . protected &&
704
- ! isPropertyTermScopedContext ) {
710
+ // Check for overriding protected terms
711
+ if ( previousMapping && previousMapping . protected && ! overrideProtected ) {
705
712
// force new term to continue to be protected and see if the mappings would
706
713
// be equal
707
714
activeCtx . protected [ term ] = true ;
@@ -776,11 +783,6 @@ function _expandIri(activeCtx, value, relativeTo, localCtx, defined, options) {
776
783
api . createTermDefinition ( activeCtx , localCtx , value , defined , options ) ;
777
784
}
778
785
779
- // if context is from a property term scoped context composed with a
780
- // type-scoped context, then use previous context instead
781
- if ( activeCtx . isPropertyTermScoped && activeCtx . previousContext ) {
782
- activeCtx = activeCtx . previousContext ;
783
- }
784
786
785
787
relativeTo = relativeTo || { } ;
786
788
if ( relativeTo . vocab ) {
@@ -862,7 +864,7 @@ api.getInitialContext = options => {
862
864
inverse : null ,
863
865
getInverse : _createInverseContext ,
864
866
clone : _cloneActiveContext ,
865
- revertTypeScopedContext : _revertTypeScopedContext ,
867
+ revertToPreviousContext : _revertToPreviousContext ,
866
868
protected : { }
867
869
} ;
868
870
// TODO: consider using LRU cache instead
@@ -1039,10 +1041,9 @@ api.getInitialContext = options => {
1039
1041
child . getInverse = this . getInverse ;
1040
1042
child . protected = util . clone ( this . protected ) ;
1041
1043
if ( this . previousContext ) {
1042
- child . isPropertyTermScoped = this . previousContext . isPropertyTermScoped ;
1043
1044
child . previousContext = this . previousContext . clone ( ) ;
1044
1045
}
1045
- child . revertTypeScopedContext = this . revertTypeScopedContext ;
1046
+ child . revertToPreviousContext = this . revertToPreviousContext ;
1046
1047
if ( '@language' in this ) {
1047
1048
child [ '@language' ] = this [ '@language' ] ;
1048
1049
}
@@ -1056,7 +1057,7 @@ api.getInitialContext = options => {
1056
1057
* Reverts any type-scoped context in this active context to the previous
1057
1058
* context.
1058
1059
*/
1059
- function _revertTypeScopedContext ( ) {
1060
+ function _revertToPreviousContext ( ) {
1060
1061
if ( ! this . previousContext ) {
1061
1062
return this ;
1062
1063
}
0 commit comments