Skip to content

Update rdf-canonize, node versions, and more. #548

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

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
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
20 changes: 10 additions & 10 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -8,9 +8,9 @@ jobs:
timeout-minutes: 10
strategy:
matrix:
node-version: [18.x]
node-version: [20.x]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
@@ -23,9 +23,9 @@ jobs:
timeout-minutes: 10
strategy:
matrix:
node-version: [14.x, 16.x, 18.x, 20.x]
node-version: [18.x, 20.x]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
@@ -41,10 +41,10 @@ jobs:
timeout-minutes: 10
strategy:
matrix:
node-version: [16.x]
node-version: [20.x]
bundler: [webpack, browserify]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
@@ -62,9 +62,9 @@ jobs:
timeout-minutes: 10
strategy:
matrix:
node-version: [16.x]
node-version: [20.x]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
@@ -78,9 +78,9 @@ jobs:
timeout-minutes: 10
strategy:
matrix:
node-version: [18.x]
node-version: [20.x]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
# jsonld ChangeLog

## 9.0.0 - 2023-xx-xx

### Changed
- **BREAKING**: Drop support for Node.js < 18.
- **BREAKING**: Upgrade dependencies.
- `@digitalbazaar/http-client@4`.
- `canonicalize@2`.
- `rdf-canonize@4`: See the [rdf-canonize][] 4.0.0 changelog for
**important** changes and upgrade notes. Of note:
- The `URDNA2015` default algorithm has been changed to `RDFC-1.0` from
[rdf-canon][].
- Complexity control defaults `maxWorkFactor` or `maxDeepIterations` may
need to be adjusted to process graphs with certain blank node constructs.
- A `signal` option is available to use an `AbortSignal` to limit resource
usage.
- The internal digest algorithm can be changed.

### Removed
- **BREAKING**: Remove `application/nquads` alias for `application/n-quads`.

## 8.3.2 - 2023-12-06

### Fixed
43 changes: 31 additions & 12 deletions lib/fromRdf.js
Original file line number Diff line number Diff line change
@@ -89,7 +89,7 @@ api.fromRDF = async (
const nodeMap = graphMap[name];

// get subject, predicate, object
const s = quad.subject.value;
const s = _nodeId(quad.subject);
const p = quad.predicate.value;
const o = quad.object;

@@ -98,13 +98,14 @@ api.fromRDF = async (
}
const node = nodeMap[s];

const objectIsNode = o.termType.endsWith('Node');
if(objectIsNode && !(o.value in nodeMap)) {
nodeMap[o.value] = {'@id': o.value};
const objectNodeId = _nodeId(o);
const objectIsNode = !!objectNodeId;
if(objectIsNode && !(objectNodeId in nodeMap)) {
nodeMap[objectNodeId] = {'@id': objectNodeId};
}

if(p === RDF_TYPE && !useRdfType && objectIsNode) {
_addValue(node, '@type', o.value, {propertyIsArray: true});
_addValue(node, '@type', objectNodeId, {propertyIsArray: true});
continue;
}

@@ -114,9 +115,9 @@ api.fromRDF = async (
// object may be an RDF list/partial list node but we can't know easily
// until all triples are read
if(objectIsNode) {
if(o.value === RDF_NIL) {
if(objectNodeId === RDF_NIL) {
// track rdf:nil uniquely per graph
const object = nodeMap[o.value];
const object = nodeMap[objectNodeId];
if(!('usages' in object)) {
object.usages = [];
}
@@ -125,12 +126,12 @@ api.fromRDF = async (
property: p,
value
});
} else if(o.value in referencedOnce) {
} else if(objectNodeId in referencedOnce) {
// object referenced more than once
referencedOnce[o.value] = false;
referencedOnce[objectNodeId] = false;
} else {
// keep track of single reference
referencedOnce[o.value] = {
referencedOnce[objectNodeId] = {
node,
property: p,
value
@@ -303,8 +304,9 @@ api.fromRDF = async (
*/
function _RDFToObject(o, useNativeTypes, rdfDirection, options) {
// convert NamedNode/BlankNode object to JSON-LD
if(o.termType.endsWith('Node')) {
return {'@id': o.value};
const nodeId = _nodeId(o);
if(nodeId) {
return {'@id': nodeId};
}

// convert literal to JSON-LD
@@ -397,3 +399,20 @@ function _RDFToObject(o, useNativeTypes, rdfDirection, options) {

return rval;
}

/**
* Return id for a term. Handles BlankNodes and NamedNodes. Adds a '_:' prefix
* for BlanksNodes.
*
* @param term a term object.
*
* @return the Node term id or null.
*/
function _nodeId(term) {
if(term.termType === 'NamedNode') {
return term.value;
} else if(term.termType === 'BlankNode') {
return '_:' + term.value;
}
return null;
}
53 changes: 30 additions & 23 deletions lib/jsonld.js
Original file line number Diff line number Diff line change
@@ -523,33 +523,39 @@ jsonld.link = async function(input, ctx, options) {
/**
* Performs RDF dataset normalization on the given input. The input is JSON-LD
* unless the 'inputFormat' option is used. The output is an RDF dataset
* unless the 'format' option is used.
* unless a non-null 'format' option is used.
*
* Note: Canonicalization sets `safe` to `true` and `base` to `null` by
* default in order to produce safe outputs and "fail closed" by default. This
* is different from the other API transformations in this version which
* allow unsafe defaults (for cryptographic usage) in order to comply with the
* JSON-LD 1.1 specification.
*
* @param input the input to normalize as JSON-LD or as a format specified by
* the 'inputFormat' option.
* @param input the input to normalize as JSON-LD given as an RDF dataset or as
* a format specified by the 'inputFormat' option.
* @param [options] the options to use:
* [algorithm] the normalization algorithm to use, `URDNA2015` or
* `URGNA2012` (default: `URDNA2015`).
* [base] the base IRI to use (default: `null`).
* [expandContext] a context to expand with.
* [skipExpansion] true to assume the input is expanded and skip
* expansion, false not to, defaults to false. Some well-formed
* and safe-mode checks may be omitted.
* [inputFormat] the format if input is not JSON-LD:
* 'application/n-quads' for N-Quads.
* [format] the format if output is a string:
* 'application/n-quads' for N-Quads.
* [inputFormat] the input format. null for a JSON-LD object,
* 'application/n-quads' for N-Quads. (default: null)
* [format] the output format. null for an RDF dataset,
* 'application/n-quads' for an N-Quads string. (default: N-Quads)
* [documentLoader(url, options)] the document loader.
* [useNative] true to use a native canonize algorithm
* [rdfDirection] null or 'i18n-datatype' to support RDF
* transformation of @direction (default: null).
* [safe] true to use safe mode. (default: true).
* [canonizeOptions] options to pass to rdf-canonize canonize(). See
* rdf-canonize for more details. Commonly used options, and their
* defaults, are:
* algorithm="RDFC-1.0",
* messageDigestAlgorithm="sha256",
* canonicalIdMap,
* maxWorkFactor=1,
* maxDeepIterations=-1,
* and signal=null.
* [contextResolver] internal use only.
*
* @return a Promise that resolves to the normalized output.
@@ -559,18 +565,21 @@ jsonld.normalize = jsonld.canonize = async function(input, options) {
throw new TypeError('Could not canonize, too few arguments.');
}

// set default options
// set toRDF options
options = _setDefaults(options, {
base: _isString(input) ? input : null,
algorithm: 'URDNA2015',
skipExpansion: false,
safe: true,
contextResolver: new ContextResolver(
{sharedCache: _resolvedContextCache})
});

// set canonize options
const canonizeOptions = Object.assign({}, {
algorithm: 'RDFC-1.0'
}, options.canonizeOptions || null);

if('inputFormat' in options) {
if(options.inputFormat !== 'application/n-quads' &&
options.inputFormat !== 'application/nquads') {
if(options.inputFormat !== 'application/n-quads') {
throw new JsonLdError(
'Unknown canonicalization input format.',
'jsonld.CanonizeError');
@@ -579,17 +588,18 @@ jsonld.normalize = jsonld.canonize = async function(input, options) {
const parsedInput = NQuads.parse(input);

// do canonicalization
return canonize.canonize(parsedInput, options);
return canonize.canonize(parsedInput, canonizeOptions);
}

// convert to RDF dataset then do normalization
const opts = {...options};
delete opts.format;
delete opts.canonizeOptions;
opts.produceGeneralizedRdf = false;
const dataset = await jsonld.toRDF(input, opts);

// do canonicalization
return canonize.canonize(dataset, options);
return canonize.canonize(dataset, canonizeOptions);
};

/**
@@ -653,8 +663,8 @@ jsonld.fromRDF = async function(dataset, options) {
* [skipExpansion] true to assume the input is expanded and skip
* expansion, false not to, defaults to false. Some well-formed
* and safe-mode checks may be omitted.
* [format] the format to use to output a string:
* 'application/n-quads' for N-Quads.
* [format] the output format. null for an RDF dataset,
* 'application/n-quads' for an N-Quads string. (default: null)
* [produceGeneralizedRdf] true to output generalized RDF, false
* to produce only standard RDF (default: false).
* [documentLoader(url, options)] the document loader.
@@ -672,7 +682,6 @@ jsonld.toRDF = async function(input, options) {

// set default options
options = _setDefaults(options, {
base: _isString(input) ? input : '',
skipExpansion: false,
contextResolver: new ContextResolver(
{sharedCache: _resolvedContextCache})
@@ -690,8 +699,7 @@ jsonld.toRDF = async function(input, options) {
// output RDF dataset
const dataset = _toRDF(expanded, options);
if(options.format) {
if(options.format === 'application/n-quads' ||
options.format === 'application/nquads') {
if(options.format === 'application/n-quads') {
return NQuads.serialize(dataset);
}
throw new JsonLdError(
@@ -997,7 +1005,6 @@ jsonld.unregisterRDFParser = function(contentType) {

// register the N-Quads RDF parser
jsonld.registerRDFParser('application/n-quads', NQuads.parse);
jsonld.registerRDFParser('application/nquads', NQuads.parse);

/* URL API */
jsonld.url = require('./url');
Loading