-
Notifications
You must be signed in to change notification settings - Fork 18
/
JSONLDResolver.js
89 lines (80 loc) · 2.98 KB
/
JSONLDResolver.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import { ContextParser } from 'jsonld-context-parser';
import { namedNode } from '@rdfjs/data-model';
import { lazyThenable } from './promiseUtils';
/**
* Resolves property names of a path
* to their corresponding IRIs through a JSON-LD context.
*/
export default class JSONLDResolver {
_context = Promise.resolve({});
/**
* Creates a new resolver for the given context(s).
*/
constructor(...contexts) {
this.extendContext(...contexts);
}
/**
* The JSON-LD resolver supports all string properties.
*/
supports(property) {
return typeof property === 'string';
}
/**
* Resolves the property by extending the query path with it.
*/
resolve(property, pathData) {
const predicate = lazyThenable(() => this.expandProperty(property));
const reverse = lazyThenable(() => this._context.then(context =>
context[property] && context[property]['@reverse']));
const resultsCache = this.getResultsCache(pathData, predicate, reverse);
return pathData.extendPath({ property, predicate, resultsCache, reverse, asFunction: this.apply });
}
/**
* Function for fixing the object values of the predicate.
* Extends the proxy with the new values.
*/
apply(pathData, args) {
if (!args || args.length === 0)
throw new Error('At least 1 argument is required');
if (args.some(arg => !arg.termType))
throw new Error('Args need to be RDF objects');
pathData.values = args;
// Prevent children from inheriting this apply function
delete pathData.asFunction;
// No extendPath call since we don't want to add a chain so return original proxy
return pathData.proxy;
}
/**
* Expands a JSON property key into a full IRI.
*/
async expandProperty(property) {
// JavaScript requires keys containing colons to be quoted,
// so prefixed names would need to written as path['foaf:knows'].
// We thus allow writing path.foaf_knows or path.foaf$knows instead.
property = property.replace(/^([a-z][a-z0-9]*)[_$]/i, '$1:');
// Expand the property to a full IRI
const context = await this._context;
const expandedProperty = ContextParser.expandTerm(property, context, true);
if (!ContextParser.isValidIri(expandedProperty))
throw new Error(`The JSON-LD context cannot expand the '${property}' property`);
return namedNode(expandedProperty);
}
/**
* Extends the current JSON-LD context with the given context(s).
*/
async extendContext(...contexts) {
await (this._context = this._context.then(currentContext =>
new ContextParser().parse([currentContext, ...contexts])));
}
/**
* Gets the results cache for the given predicate.
*/
getResultsCache(pathData, predicate, reverse) {
let { propertyCache } = pathData;
return propertyCache && lazyThenable(async () => {
// Preloading does not work with reversed predicates
propertyCache = !(await reverse) && await propertyCache;
return propertyCache && propertyCache[(await predicate).value];
});
}
}