Skip to content

Miscellaneous changes #354

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jan 25, 2020
Merged
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
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# jsonld ChangeLog

### Fixed
- More support for `"@type": "@none"`.
- JSON literal value handling issues (`null` and `[]`).
- Always pass `typeScopedContext` to `_expandObject`.
- Allow a keyword to exist when expanding in `_expandObject` when the key is
`@included` or `@type`.
- Improve `isDouble` to look for big integers.
- URI `removeDotSegments` only ensures preceding '/' if was already absolute.
- Do minimal checking to see if IRIs are valid by making sure they contain no
whitespace.
- Terms of the form of an IRI must map to the same IRI.
- Terms of the form of a relative IRI may not be used as prefixes.

### Changed
- Keep term definitions mapping to null so they may be protected.
- **NOTE**: `LINK_HEADER_REL` in `lib/constants.js` has been deprecated and
renamed to `LINK_HEADER_CONTEXT`. It remains for now but will be removed in a
future release.

## 2.0.2 - 2020-01-17

### Fixed
Expand Down
3 changes: 3 additions & 0 deletions lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ const RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
const XSD = 'http://www.w3.org/2001/XMLSchema#';

module.exports = {
// TODO: Deprecated and will be removed later. Use LINK_HEADER_CONTEXT.
LINK_HEADER_REL: 'http://www.w3.org/ns/json-ld#context',

LINK_HEADER_CONTEXT: 'http://www.w3.org/ns/json-ld#context',

RDF,
RDF_LIST: RDF + 'List',
RDF_FIRST: RDF + 'first',
Expand Down
39 changes: 27 additions & 12 deletions lib/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -365,16 +365,9 @@ api.createTermDefinition = ({
activeCtx.mappings.delete(term);
}

// clear context entry
if(value === null || (_isObject(value) && value['@id'] === null)) {
activeCtx.mappings.set(term, null);
defined.set(term, true);
return;
}

// convert short-hand value to object w/@id
let simpleTerm = false;
if(_isString(value)) {
if(_isString(value) || value === null) {
simpleTerm = true;
value = {'@id': value};
}
Expand Down Expand Up @@ -448,13 +441,16 @@ api.createTermDefinition = ({
mapping.reverse = true;
} else if('@id' in value) {
let id = value['@id'];
if(!_isString(id)) {
if(id && !_isString(id)) {
throw new JsonLdError(
'Invalid JSON-LD syntax; a @context @id value must be an array ' +
'of strings or a string.',
'jsonld.SyntaxError', {code: 'invalid IRI mapping', context: localCtx});
}
if(id !== term) {
if(id === null) {
// reserve a null term, which may be protected
mapping['@id'] = null;
} else if(id !== term) {
// expand and add @id mapping
id = _expandIri(
activeCtx, id, {vocab: true, base: false}, localCtx, defined, options);
Expand All @@ -465,6 +461,22 @@ api.createTermDefinition = ({
'jsonld.SyntaxError',
{code: 'invalid IRI mapping', context: localCtx});
}

// if term has the form of an IRI it must map the same
if(term.match(/(?::[^:])|\//)) {
const termDefined = new Map(defined).set(term, true);
const termIri = _expandIri(
activeCtx, term, {vocab: true, base: false},
localCtx, termDefined, options);
if(termIri !== id) {
throw new JsonLdError(
'Invalid JSON-LD syntax; term in form of IRI must ' +
'expand to definition.',
'jsonld.SyntaxError',
{code: 'invalid IRI mapping', context: localCtx});
}
}

mapping['@id'] = id;
// indicate if this term may be used as a compact IRI prefix
mapping._prefix = (simpleTerm &&
Expand Down Expand Up @@ -685,7 +697,7 @@ api.createTermDefinition = ({

// term may be used as a prefix
if('@prefix' in value) {
if(mapping._termHasColon) {
if(term.match(/:|\//)) {
throw new JsonLdError(
'Invalid JSON-LD syntax; @context @prefix used on a compact IRI term',
'jsonld.SyntaxError',
Expand Down Expand Up @@ -808,7 +820,7 @@ function _expandIri(activeCtx, value, relativeTo, localCtx, defined, options) {
return null;
}

if(mapping) {
if(_isObject(mapping) && '@id' in mapping) {
// value is a term
return mapping['@id'];
}
Expand Down Expand Up @@ -928,6 +940,9 @@ api.getInitialContext = options => {
let container = mapping['@container'] || '@none';
container = [].concat(container).sort().join('');

if(mapping['@id'] === null) {
continue;
}
// iterate over every IRI in the mapping
const ids = _asArray(mapping['@id']);
for(const iri of ids) {
Expand Down
23 changes: 17 additions & 6 deletions lib/documentLoaders/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
'use strict';

const {parseLinkHeader, buildHeaders} = require('../util');
const {LINK_HEADER_REL} = require('../constants');
const {LINK_HEADER_CONTEXT} = require('../constants');
const JsonLdError = require('../JsonLdError');
const RequestQueue = require('../RequestQueue');
const {prependBase} = require('../url');

/**
* Creates a built-in node document loader.
Expand Down Expand Up @@ -61,6 +62,7 @@ module.exports = ({
}

let result;
let alternate = null;
try {
result = await _request(request, {
url,
Expand Down Expand Up @@ -95,21 +97,30 @@ module.exports = ({
if(res.headers.link &&
res.headers['content-type'] !== 'application/ld+json') {
// only 1 related link header permitted
const linkHeader = parseLinkHeader(res.headers.link)[LINK_HEADER_REL];
if(Array.isArray(linkHeader)) {
const linkHeaders = parseLinkHeader(res.headers.link);
const linkedContext = linkHeaders[LINK_HEADER_CONTEXT];
if(Array.isArray(linkedContext)) {
throw new JsonLdError(
'URL could not be dereferenced, it has more than one associated ' +
'HTTP Link Header.',
'jsonld.InvalidUrl',
{code: 'multiple context link headers', url});
}
if(linkHeader) {
doc.contextUrl = linkHeader.target;
if(linkedContext) {
doc.contextUrl = linkedContext.target;
}

// "alternate" link header is a redirect
alternate = linkHeaders['alternate'];
if(alternate &&
alternate.type == 'application/ld+json' &&
!(res.headers['content-type'] || '').match(/^application\/(\w*\+)?json$/)) {
res.headers.location = prependBase(url, alternate.target);
}
}

// handle redirect
if(res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
if((alternate || res.statusCode >= 300 && res.statusCode < 400) && res.headers.location) {
if(redirects.length === maxRedirects) {
throw new JsonLdError(
'URL could not be dereferenced; there were too many redirects.',
Expand Down
21 changes: 16 additions & 5 deletions lib/documentLoaders/xhr.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
'use strict';

const {parseLinkHeader, buildHeaders} = require('../util');
const {LINK_HEADER_REL} = require('../constants');
const {LINK_HEADER_CONTEXT} = require('../constants');
const JsonLdError = require('../JsonLdError');
const RequestQueue = require('../RequestQueue');
const {prependBase} = require('../url');

const REGEX_LINK_HEADER = /(^|(\r\n))link:/i;

Expand Down Expand Up @@ -65,6 +66,7 @@ module.exports = ({
}

const doc = {contextUrl: null, documentUrl: url, document: req.response};
let alternate = null;

// handle Link Header (avoid unsafe header warning by existence testing)
const contentType = req.getResponseHeader('Content-Type');
Expand All @@ -74,16 +76,25 @@ module.exports = ({
}
if(linkHeader && contentType !== 'application/ld+json') {
// only 1 related link header permitted
linkHeader = parseLinkHeader(linkHeader)[LINK_HEADER_REL];
if(Array.isArray(linkHeader)) {
const linkHeaders = parseLinkHeader(res.headers.link);
const linkedContext = linkHeaders[LINK_HEADER_CONTEXT];
if(Array.isArray(linkedContext)) {
throw new JsonLdError(
'URL could not be dereferenced, it has more than one ' +
'associated HTTP Link Header.',
'jsonld.InvalidUrl',
{code: 'multiple context link headers', url});
}
if(linkHeader) {
doc.contextUrl = linkHeader.target;
if(linkedContext) {
doc.contextUrl = linkedContext.target;
}

// "alternate" link header is a redirect
alternate = linkHeaders['alternate'];
if(alternate &&
alternate.type == 'application/ld+json' &&
!(contentType || '').match(/^application\/(\w*\+)?json$/)) {
doc = await loader(prependBase(url, alternate.target));
}
}

Expand Down
5 changes: 4 additions & 1 deletion lib/expand.js
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,9 @@ async function _expandObject({
'property.', 'jsonld.SyntaxError',
{code: 'invalid reverse property map', value});
}
if(expandedProperty in expandedParent) {
if(expandedProperty in expandedParent &&
expandedProperty !== '@included' &&
expandedProperty !== '@type') {
throw new JsonLdError(
'Invalid JSON-LD syntax; colliding keywords detected.',
'jsonld.SyntaxError',
Expand Down Expand Up @@ -821,6 +823,7 @@ async function _expandObject({
expandedParent,
options,
insideList,
typeScopedContext,
typeKey,
expansionMap});
}
Expand Down
3 changes: 2 additions & 1 deletion lib/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ api.isBoolean = v => (typeof v === 'boolean' ||
*
* @return true if the value is a double, false if not.
*/
api.isDouble = v => api.isNumber(v) && String(v).indexOf('.') !== -1;
api.isDouble = v => api.isNumber(v) &&
(String(v).indexOf('.') !== -1 || Math.abs(v) >= 1e21);

/**
* Returns true if the given value is an empty Object.
Expand Down
6 changes: 3 additions & 3 deletions lib/url.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,8 @@ api.removeDotSegments = path => {
output.push(next);
}

// ensure output has leading /
if(output.length > 0 && output[0] !== '') {
// if path was absolute, ensure output has leading /
if(path[0] === '/' && output.length > 0 && output[0] !== '') {
output.unshift('');
}
if(output.length === 1 && output[0] === '') {
Expand All @@ -277,7 +277,7 @@ api.removeDotSegments = path => {
// http://jmrware.com/articles/2009/uri_regexp/URI_regex.html

// regex to check for absolute IRI (starting scheme and ':') or blank node IRI
const isAbsoluteRegex = /^([A-Za-z][A-Za-z0-9+-.]*|_):/;
const isAbsoluteRegex = /^([A-Za-z][A-Za-z0-9+-.]*|_):[^\s]*$/;

/**
* Returns true if the given value is an absolute IRI or blank node IRI, false
Expand Down
Loading