@@ -45,7 +45,8 @@ api.cache = new ActiveContextCache();
45
45
*
46
46
* @return the new active context.
47
47
*/
48
- api . process = ( { activeCtx, localCtx, options} ) => {
48
+ api . process = ( { activeCtx, localCtx, options,
49
+ propertyTermDefinition = false } ) => {
49
50
// normalize local context to an array of @context objects
50
51
if ( _isObject ( localCtx ) && '@context' in localCtx &&
51
52
_isArray ( localCtx [ '@context' ] ) ) {
@@ -66,6 +67,16 @@ api.process = ({activeCtx, localCtx, options}) => {
66
67
67
68
// reset to initial context
68
69
if ( ctx === null ) {
70
+ // We can't nullify if there are sealed terms and we're
71
+ // not in a term definition
72
+ if ( ! propertyTermDefinition &&
73
+ Object . keys ( activeCtx . sealed ) . length !== 0 ) {
74
+ throw new JsonLdError (
75
+ 'Tried to nullify a context with sealed terms outside of ' +
76
+ 'a term definition.' ,
77
+ 'jsonld.SyntaxError' ,
78
+ { code : 'invalid context nullification' } ) ;
79
+ }
69
80
rval = activeCtx = api . getInitialContext ( options ) ;
70
81
continue ;
71
82
}
@@ -182,6 +193,11 @@ api.process = ({activeCtx, localCtx, options}) => {
182
193
defined [ '@language' ] = true ;
183
194
}
184
195
196
+ // handle @sealed ; determine whether this sub-context is declaring
197
+ // all its terms to be "sealed" (exceptions can be made on a
198
+ // per-definition basis)
199
+ defined [ '@sealed' ] = ctx [ '@sealed' ] || false ;
200
+
185
201
// process all other keys
186
202
for ( const key in ctx ) {
187
203
api . createTermDefinition ( rval , ctx , key , defined ) ;
@@ -235,6 +251,15 @@ api.createTermDefinition = (activeCtx, localCtx, term, defined) => {
235
251
{ code : 'invalid term definition' , context : localCtx } ) ;
236
252
}
237
253
254
+ // Is this term currently sealed?
255
+ // Then redefining it here is an error!
256
+ if ( activeCtx . sealed [ term ] ) {
257
+ throw new JsonLdError (
258
+ 'Invalid JSON-LD syntax; tried to redefine a sealed term.' ,
259
+ 'jsonld.SyntaxError' ,
260
+ { code : 'sealed term redefinition' , context : localCtx , term : term } ) ;
261
+ }
262
+
238
263
// remove old mapping
239
264
if ( activeCtx . mappings [ term ] ) {
240
265
delete activeCtx . mappings [ term ] ;
@@ -274,7 +299,7 @@ api.createTermDefinition = (activeCtx, localCtx, term, defined) => {
274
299
275
300
// JSON-LD 1.1 support
276
301
if ( api . processingMode ( activeCtx , 1.1 ) ) {
277
- validKeys . push ( '@context' , '@nest' , '@prefix' ) ;
302
+ validKeys . push ( '@context' , '@nest' , '@prefix' , '@sealed' ) ;
278
303
}
279
304
280
305
for ( const kw in value ) {
@@ -379,6 +404,17 @@ api.createTermDefinition = (activeCtx, localCtx, term, defined) => {
379
404
}
380
405
}
381
406
407
+ // Handle term sealing
408
+ if ( '@sealed' in value ) {
409
+ if ( value [ '@sealed' ] ) {
410
+ activeCtx . sealed [ term ] = true ;
411
+ }
412
+ } else if ( '@sealed' in defined ) {
413
+ if ( defined [ '@sealed' ] ) {
414
+ activeCtx . sealed [ term ] = true ;
415
+ }
416
+ }
417
+
382
418
// IRI mapping now defined
383
419
defined [ term ] = true ;
384
420
@@ -650,7 +686,8 @@ api.getInitialContext = options => {
650
686
mappings : { } ,
651
687
inverse : null ,
652
688
getInverse : _createInverseContext ,
653
- clone : _cloneActiveContext
689
+ clone : _cloneActiveContext ,
690
+ sealed : { }
654
691
} ;
655
692
// TODO: consider using LRU cache instead
656
693
if ( INITIAL_CONTEXT_CACHE . size === INITIAL_CONTEXT_CACHE_MAX_SIZE ) {
@@ -826,6 +863,7 @@ api.getInitialContext = options => {
826
863
child . clone = this . clone ;
827
864
child . inverse = null ;
828
865
child . getInverse = this . getInverse ;
866
+ child . sealed = util . clone ( this . sealed ) ;
829
867
if ( '@language' in this ) {
830
868
child [ '@language' ] = this [ '@language' ] ;
831
869
}
@@ -937,6 +975,7 @@ api.isKeyword = v => {
937
975
case '@preserve' :
938
976
case '@requireAll' :
939
977
case '@reverse' :
978
+ case '@sealed' :
940
979
case '@set' :
941
980
case '@type' :
942
981
case '@value' :
0 commit comments