tripledoc
Version:
Library to read, create and update documents on a Solid Pod
1,111 lines (1,093 loc) • 55.5 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var LinkHeader = _interopDefault(require('http-link-header'));
var SolidAuthClient = _interopDefault(require('solid-auth-client'));
var n3 = require('n3');
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __awaiter(thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
function __generator(thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
}
/**
* @param quads Triples that should be serialised to Turtle
* @internal Utility method for internal use by Tripledoc; not part of the public API.
*/
function triplesToTurtle(quads) {
return __awaiter(this, void 0, void 0, function () {
var format, writer, triples, writePromise, rawTurtle;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
format = 'text/turtle';
writer = new n3.Writer({ format: format });
triples = quads.map(function (quad) { return n3.DataFactory.triple(quad.subject, quad.predicate, quad.object); });
writer.addQuads(triples);
writePromise = new Promise(function (resolve, reject) {
writer.end(function (error, result) {
/* istanbul ignore if [n3.js doesn't actually pass an error nor a result, apparently: https://github.com/rdfjs/N3.js/blob/62682e48c02d8965b4d728cb5f2cbec6b5d1b1b8/src/N3Writer.js#L290] */
if (error) {
return reject(error);
}
resolve(result);
});
});
return [4 /*yield*/, writePromise];
case 1:
rawTurtle = _a.sent();
return [2 /*return*/, rawTurtle];
}
});
});
}
/**
* @param raw Turtle that should be parsed into Triples
* @internal Utility method for internal use by Tripledoc; not part of the public API.
*/
function turtleToTriples(raw, documentRef) {
return __awaiter(this, void 0, void 0, function () {
var format, parser, parsingPromise;
return __generator(this, function (_a) {
format = 'text/turtle';
parser = new n3.Parser({ format: format, baseIRI: documentRef });
parsingPromise = new Promise(function (resolve, reject) {
var parsedTriples = [];
parser.parse(raw, function (error, triple, _prefixes) {
if (error) {
return reject(error);
}
if (triple) {
parsedTriples.push(triple);
}
else {
resolve(parsedTriples);
}
});
});
return [2 /*return*/, parsingPromise];
});
});
}
/**
* Utility function that gets Triples located at a URL
*
* @param url Location of the Document contains the Triples.
* @returns Promise that resolves with the Triples
* @internal Should not be used by library consumers directly.
*/
/* istanbul ignore next Just a thin wrapper around solid-auth-client, yet cumbersome to test due to side effects */
function get(url) {
return __awaiter(this, void 0, void 0, function () {
var response;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, SolidAuthClient.fetch(url, {
headers: {
Accept: 'text/turtle',
},
})];
case 1:
response = _a.sent();
return [2 /*return*/, response];
}
});
});
}
/**
* Utility function that gets a URL's metadata
*
* @param url Location of the Document to get the metadata of
* @returns Promise that resolves with the Response
* @internal Should not be used by library consumers directly.
*/
/* istanbul ignore next Just a thin wrapper around solid-auth-client, yet cumbersome to test due to side effects */
function head(url) {
return __awaiter(this, void 0, void 0, function () {
var response;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, SolidAuthClient.fetch(url, {
method: 'HEAD',
})];
case 1:
response = _a.sent();
return [2 /*return*/, response];
}
});
});
}
/**
* Utility function that sends a PATCH request to the Pod to update a Document
*
* @param url Location of the Document that contains the Triples to delete, and should have the Triples to add.
* @param triplesToDelete Triples currently present on the Pod that should be deleted.
* @param triplesToAdd Triples not currently present on the Pod that should be added.
* @returns Promise that resolves when the update was executed successfully, and rejects if not.
* @internal Should not be used by library consumers directly.
*/
/* istanbul ignore next Just a thin wrapper around solid-auth-client, yet cumbersome to test due to side effects */
function update(url, triplesToDelete, triplesToAdd) {
return __awaiter(this, void 0, void 0, function () {
var rawTriplesToDelete, rawTriplesToAdd, deleteStatement, insertStatement, response;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, triplesToTurtle(triplesToDelete)];
case 1:
rawTriplesToDelete = _a.sent();
return [4 /*yield*/, triplesToTurtle(triplesToAdd)];
case 2:
rawTriplesToAdd = _a.sent();
deleteStatement = (triplesToDelete.length > 0)
? "DELETE DATA {" + rawTriplesToDelete + "};"
: '';
insertStatement = (triplesToAdd.length > 0)
? "INSERT DATA {" + rawTriplesToAdd + "};"
: '';
return [4 /*yield*/, SolidAuthClient.fetch(url, {
method: 'PATCH',
body: deleteStatement + " " + insertStatement,
headers: {
'Content-Type': 'application/sparql-update',
},
})];
case 3:
response = _a.sent();
return [2 /*return*/, response];
}
});
});
}
/**
* Utility function that sends a PUT request to the Pod to create a new Document
*
* @param url URL of the Document that should be created.
* @param triplesToAdd Triples that should be added to the Document.
* @returns Promise that resolves with the response when the Document was created successfully, and rejects if not.
* @internal Should not be used by library consumers directly.
*/
/* istanbul ignore next Just a thin wrapper around solid-auth-client, yet cumbersome to test due to side effects */
function create(url, triplesToAdd) {
return __awaiter(this, void 0, void 0, function () {
var rawTurtle, response;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, triplesToTurtle(triplesToAdd)];
case 1:
rawTurtle = _a.sent();
return [4 /*yield*/, SolidAuthClient.fetch(url, {
method: 'PUT',
body: rawTurtle,
headers: {
'Content-Type': 'text/turtle',
'If-None-Match': '*',
},
})];
case 2:
response = _a.sent();
return [2 /*return*/, response];
}
});
});
}
/**
* Utility function that sends a POST request to a Container in the Pod to create a new Document
*
* @param containerUrl URL of the Container in which the Document should be created.
* @param triplesToAdd Triples that should be added to the Document.
* @returns Promise that resolves with the response when the Document was created successfully, and rejects if not.
* @internal Should not be used by library consumers directly.
*/
/* istanbul ignore next Just a thin wrapper around solid-auth-client, yet cumbersome to test due to side effects */
function createInContainer(containerUrl, triplesToAdd, options) {
if (options === void 0) { options = {}; }
return __awaiter(this, void 0, void 0, function () {
var rawTurtle, headers, response;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, triplesToTurtle(triplesToAdd)];
case 1:
rawTurtle = _a.sent();
headers = {
'Content-Type': 'text/turtle',
};
if (options.slugSuggestion) {
headers.slug = options.slugSuggestion;
}
return [4 /*yield*/, SolidAuthClient.fetch(containerUrl, {
method: 'POST',
body: rawTurtle,
headers: headers,
})];
case 2:
response = _a.sent();
return [2 /*return*/, response];
}
});
});
}
/* istanbul ignore next: A simple wrapper to make N3 conform with a subset of the RDF/JS Dataset
interface; should contain no business logic. */
function toRdfjsDataset(store) {
var addAll = function (quads) {
var quadsAsArray = Array.isArray(quads) ? quads : quads.toArray();
store.addQuads(quadsAsArray);
return dataset;
};
var match = function (subject, predicate, object, graph) {
var notUndefinedSubject = (typeof subject === 'undefined') ? null : subject;
var notUndefinedPredicate = (typeof predicate === 'undefined') ? null : predicate;
var notUndefinedObject = (typeof object === 'undefined') ? null : object;
var notUndefinedGraph = (typeof graph === 'undefined') ? null : graph;
return {
toArray: function () { return store.getQuads(notUndefinedSubject, notUndefinedPredicate, notUndefinedObject, notUndefinedGraph); },
};
};
var toArray = function () { return store.getQuads(null, null, null, null); };
var dataset = {
addAll: addAll,
match: match,
toArray: toArray,
};
return dataset;
}
/**
* @internal
*/
function initialiseDataset() {
return toRdfjsDataset(new n3.Store());
}
/**
* @internal
*/
var DataFactory = n3.DataFactory;
/**
* @internal This is a utility method for other parts of the code, and not part of the public API.
*/
var findSubjectInDataset = function (dataset, predicateRef, objectRef) {
return findEntityInDataset(dataset, 'subject', null, predicateRef, objectRef);
};
/**
* @internal This is a utility method for other parts of the code, and not part of the public API.
*/
var findSubjectsInDataset = function (dataset, predicateRef, objectRef) {
return findEntitiesInDataset(dataset, 'subject', null, predicateRef, objectRef);
};
/**
* @internal This is a utility method for other parts of the code, and not part of the public API.
*/
var findObjectsInDataset = function (dataset, subjectRef, predicateRef) {
return findEntitiesInDataset(dataset, 'object', subjectRef, predicateRef, null);
};
/**
* @internal This is a utility method for other parts of the code, and not part of the public API.
*/
function findEntityInDataset(dataset, type, subjectRef, predicateRef, objectRef) {
var targetSubject = subjectRef ? toNode(subjectRef) : null;
var targetPredicate = predicateRef ? toNode(predicateRef) : null;
var targetObject = objectRef ? toNode(objectRef) : null;
var matchingTriples = dataset.match(targetSubject, targetPredicate, targetObject, null).toArray();
var foundTriple = matchingTriples.find(function (triple) { return (typeof triple[type] !== 'undefined'); });
return (typeof foundTriple !== 'undefined') ? normaliseEntity(foundTriple[type]) : null;
}
/**
* @internal This is a utility method for other parts of the code, and not part of the public API.
*/
function findEntitiesInDataset(dataset, type, subjectRef, predicateRef, objectRef) {
var targetSubject = subjectRef ? toNode(subjectRef) : null;
var targetPredicate = predicateRef ? toNode(predicateRef) : null;
var targetObject = objectRef ? toNode(objectRef) : null;
var matchingTriples = dataset.match(targetSubject, targetPredicate, targetObject, null).toArray();
var foundTriples = matchingTriples.filter(function (triple) { return (typeof triple[type] !== 'undefined'); });
return foundTriples.map(function (triple) { return normaliseEntity(triple[type]); }).filter(isEntity);
}
function toNode(referenceOrBlankNode) {
return (typeof referenceOrBlankNode === 'string') ? DataFactory.namedNode(referenceOrBlankNode) : referenceOrBlankNode;
}
function normaliseEntity(entity) {
if (isBlankNode(entity)) {
return entity;
}
if (isNamedNode(entity)) {
return entity.value;
}
/* istanbul ignore else: All code paths to here result in either a Node or a Literal, so we can't test it */
if (isLiteral(entity)) {
return entity;
}
/* istanbul ignore next: All code paths to here result in either a Node or a Literal, so we can't test it */
return null;
}
function isEntity(node) {
return (node !== null);
}
/**
* @internal Utility function for working with N3, which the library consumer should not need to
* be exposed to.
*/
function isNamedNode(node) {
return node.termType === 'NamedNode';
}
/**
* @internal Utility function for working with rdflib, which the library consumer should not need to
* be exposed to.
*/
function isBlankNode(node) {
return node.termType === 'BlankNode';
}
/**
* @internal Only to be called by the Document containing this subject; not a public API.
* @param document The Document this Subject is defined in.
* @param subjectRef The URL that identifies this subject.
*/
function initialiseSubject(document, subjectRef) {
var subjectNode = isBlankNode$1(subjectRef) ? subjectRef : DataFactory.namedNode(subjectRef);
var triples = (isSavedToPod(document))
? document.getStore().match(subjectNode, null, null, null).toArray()
: [];
var dataset = initialiseDataset();
dataset.addAll(triples);
var pendingAdditions = [];
var pendingDeletions = [];
var get = function (predicateNode) { return findObjectsInDataset(dataset, subjectRef, predicateNode); };
var getString = function (predicateNode) {
var objects = get(predicateNode);
var firstStringLiteral = objects.find(isStringLiteral);
if (typeof firstStringLiteral === 'undefined') {
return null;
}
return firstStringLiteral.value;
};
var getLocaleString = function (predicateNode, locale) {
var objects = get(predicateNode);
var firstStringLiteral = objects.find(generateLocaleTypeGuard(locale));
if (typeof firstStringLiteral === 'undefined') {
return null;
}
return firstStringLiteral.value;
};
var getInteger = function (predicateRef) {
var objects = get(predicateRef);
var firstIntegerLiteral = objects.find(isIntegerLiteral);
if (typeof firstIntegerLiteral === 'undefined') {
return null;
}
return fromIntegerLiteral(firstIntegerLiteral);
};
var getDecimal = function (predicateRef) {
var objects = get(predicateRef);
var firstDecimalLiteral = objects.find(isDecimalLiteral);
if (typeof firstDecimalLiteral === 'undefined') {
return null;
}
return fromDecimalLiteral(firstDecimalLiteral);
};
var getDateTime = function (predicateRef) {
var objects = get(predicateRef);
var firstDateTimeLiteral = objects.find(isDateTimeLiteral);
if (typeof firstDateTimeLiteral === 'undefined') {
return null;
}
return fromDateTimeLiteral(firstDateTimeLiteral);
};
var getLiteral = function (predicateRef) {
var objects = get(predicateRef);
var firstLiteral = objects.find(isLiteral);
if (typeof firstLiteral === 'undefined') {
return null;
}
return fromLiteral(firstLiteral);
};
var getAllStrings = function (predicateRef) {
var objects = get(predicateRef);
var literals = objects.filter(isStringLiteral);
return literals.map(fromStringLiteral);
};
var getAllLocaleStrings = function (predicateRef, locale) {
var objects = get(predicateRef);
if (locale) {
var literals_1 = objects.filter(generateLocaleTypeGuard(locale));
return literals_1.map(fromStringLiteral);
}
var literals = objects.filter(hasLocale);
return literals.map(fromLocaleStringLiteral);
};
var getAllIntegers = function (predicateRef) {
var objects = get(predicateRef);
var literals = objects.filter(isIntegerLiteral);
return literals.map(fromIntegerLiteral);
};
var getAllDecimals = function (predicateRef) {
var objects = get(predicateRef);
var literals = objects.filter(isDecimalLiteral);
return literals.map(fromDecimalLiteral);
};
var getAllDateTimes = function (predicateRef) {
var objects = get(predicateRef);
var literals = objects.filter(isDateTimeLiteral);
return literals.map(fromDateTimeLiteral);
};
var getAllLiterals = function (predicateRef) {
var objects = get(predicateRef);
var literals = objects.filter(isLiteral);
return literals.map(fromLiteral);
};
var getLocalSubject = function (predicateRef) {
var objects = get(predicateRef);
var firstRef = objects.find(isBlankNode$1);
if (typeof firstRef === 'undefined') {
return null;
}
return initialiseSubject(document, firstRef);
};
var getAllLocalSubjects = function (predicateRef) {
var objects = get(predicateRef);
var nodeRefs = objects.filter(isBlankNode$1);
return nodeRefs.map(function (localSubject) { return initialiseSubject(document, localSubject); });
};
var getRef = function (predicateRef) {
var objects = get(predicateRef);
var firstRef = objects.find(isReference);
if (typeof firstRef === 'undefined') {
return null;
}
return firstRef;
};
var getAllRefs = function (predicateRef) {
var objects = get(predicateRef);
var nodeRefs = objects.filter(isReference);
return nodeRefs;
};
var getType = function () {
return getRef('http://www.w3.org/1999/02/22-rdf-syntax-ns#type');
};
var addLiteral = function (predicateRef, literal) {
pendingAdditions.push(DataFactory.triple(subjectNode, DataFactory.namedNode(predicateRef), asLiteral(literal)));
};
var addString = function (predicateRef, literal) {
if (typeof literal !== 'string') {
throw new Error('The given value is not a string.');
}
return addLiteral(predicateRef, literal);
};
var addLocaleString = function (predicateRef, literal, locale) {
if (typeof literal !== 'string') {
throw new Error('The given value is not a string.');
}
pendingAdditions.push(DataFactory.triple(subjectNode, DataFactory.namedNode(predicateRef), DataFactory.literal(literal, locale)));
};
var addInteger = function (predicateRef, literal) {
if (typeof literal !== 'number' || !Number.isInteger(literal)) {
throw new Error('The given value is not an integer.');
}
return addLiteral(predicateRef, literal);
};
var addDecimal = function (predicateRef, literal) {
if (typeof literal !== 'number') {
throw new Error('The given value is not a decimal.');
}
pendingAdditions.push(DataFactory.triple(subjectNode, DataFactory.namedNode(predicateRef), DataFactory.literal(literal.toString(), DataFactory.namedNode('http://www.w3.org/2001/XMLSchema#decimal'))));
};
var addDateTime = function (predicateRef, literal) {
if (literal instanceof Date === false) {
throw new Error('The given value is not a DateTime.');
}
return addLiteral(predicateRef, literal);
};
var addRef = function (predicateRef, nodeRef) {
pendingAdditions.push(DataFactory.triple(subjectNode, DataFactory.namedNode(predicateRef), DataFactory.namedNode(nodeRef)));
};
var removeRef = function (predicateRef, nodeRef) {
pendingDeletions.push(DataFactory.triple(subjectNode, DataFactory.namedNode(predicateRef), DataFactory.namedNode(nodeRef)));
};
var removeLiteral = function (predicateRef, literal) {
pendingDeletions.push(DataFactory.triple(subjectNode, DataFactory.namedNode(predicateRef), asLiteral(literal)));
};
var removeString = function (predicateRef, literal) {
if (typeof literal !== 'string') {
throw new Error('The given value is not a string.');
}
return removeLiteral(predicateRef, literal);
};
var removeLocaleString = function (predicateRef, literal, locale) {
if (typeof literal !== 'string') {
throw new Error('The given value is not a string.');
}
pendingDeletions.push(DataFactory.triple(subjectNode, DataFactory.namedNode(predicateRef), DataFactory.literal(literal, locale)));
};
var removeInteger = function (predicateRef, literal) {
if (typeof literal !== 'number' || !Number.isInteger(literal)) {
throw new Error('The given value is not an integer.');
}
return removeLiteral(predicateRef, literal);
};
var removeDecimal = function (predicateRef, literal) {
if (typeof literal !== 'number') {
throw new Error('The given value is not a decimal.');
}
// We cannot re-use `removeLiteral` here because it will parse `42.0` as an integer:
pendingDeletions.push(DataFactory.triple(subjectNode, DataFactory.namedNode(predicateRef), DataFactory.literal(literal.toString(), DataFactory.namedNode('http://www.w3.org/2001/XMLSchema#decimal'))));
};
var removeDateTime = function (predicateRef, literal) {
if (literal instanceof Date === false) {
throw new Error('The given value is not a DateTime.');
}
return removeLiteral(predicateRef, literal);
};
var removeAll = function (predicateRef) {
pendingDeletions.push.apply(pendingDeletions, dataset.match(subjectNode, DataFactory.namedNode(predicateRef), null, null).toArray());
};
var clear = function () {
pendingDeletions.push.apply(pendingDeletions, getTriples());
};
var setRef = function (predicateRef, nodeRef) {
removeAll(predicateRef);
addRef(predicateRef, nodeRef);
};
var setLiteral = function (predicateRef, literal) {
removeAll(predicateRef);
addLiteral(predicateRef, literal);
};
var setString = function (predicateRef, literal) {
removeAll(predicateRef);
addString(predicateRef, literal);
};
var setLocaleString = function (predicateRef, literal, locale) {
removeAll(predicateRef);
addLocaleString(predicateRef, literal, locale);
};
var setInteger = function (predicateRef, literal) {
removeAll(predicateRef);
addInteger(predicateRef, literal);
};
var setDecimal = function (predicateRef, literal) {
removeAll(predicateRef);
addDecimal(predicateRef, literal);
};
var setDateTime = function (predicateRef, literal) {
removeAll(predicateRef);
addDateTime(predicateRef, literal);
};
var getTriples = function () { return dataset.match(subjectNode, null, null, null).toArray(); };
var asRef = function () { return isBlankNode$1(subjectRef) ? subjectRef.value : subjectRef; };
var subject = {
getDocument: function () { return document; },
getTriples: getTriples,
getString: getString,
getLocaleString: getLocaleString,
getInteger: getInteger,
getDecimal: getDecimal,
getDateTime: getDateTime,
getLiteral: getLiteral,
getAllStrings: getAllStrings,
getAllLocaleStrings: getAllLocaleStrings,
getAllIntegers: getAllIntegers,
getAllDecimals: getAllDecimals,
getAllDateTimes: getAllDateTimes,
getAllLiterals: getAllLiterals,
getLocalSubject: getLocalSubject,
getAllLocalSubjects: getAllLocalSubjects,
getRef: getRef,
getAllRefs: getAllRefs,
getType: getType,
addString: addString,
addLocaleString: addLocaleString,
addInteger: addInteger,
addDecimal: addDecimal,
addDateTime: addDateTime,
addRef: addRef,
removeAll: removeAll,
removeString: removeString,
removeLocaleString: removeLocaleString,
removeInteger: removeInteger,
removeDecimal: removeDecimal,
removeDateTime: removeDateTime,
removeRef: removeRef,
setRef: setRef,
setString: setString,
setLocaleString: setLocaleString,
setInteger: setInteger,
setDecimal: setDecimal,
setDateTime: setDateTime,
clear: clear,
getPendingTriples: function () { return [pendingDeletions, pendingAdditions]; },
asRef: asRef,
// Deprecated aliases, included for backwards compatibility:
getNodeRef: getRef,
getAllNodeRefs: getAllRefs,
addNodeRef: addRef,
addLiteral: addLiteral,
removeNodeRef: removeRef,
removeLiteral: removeLiteral,
setNodeRef: setRef,
setLiteral: setLiteral,
asNodeRef: asRef,
};
return subject;
}
function fromDateTimeLiteral(literal) {
// See https://github.com/linkeddata/rdflib.js/blob/d84af88f367b8b5f617c753d8241c5a2035458e8/src/literal.js#L87
var utcFullYear = parseInt(literal.value.substring(0, 4), 10);
var utcMonth = parseInt(literal.value.substring(5, 7), 10) - 1;
var utcDate = parseInt(literal.value.substring(8, 10), 10);
var utcHours = parseInt(literal.value.substring(11, 13), 10);
var utcMinutes = parseInt(literal.value.substring(14, 16), 10);
var utcSeconds = parseInt(literal.value.substring(17, literal.value.indexOf('Z')), 10);
var date = new Date(0);
date.setUTCFullYear(utcFullYear);
date.setUTCMonth(utcMonth);
date.setUTCDate(utcDate);
date.setUTCHours(utcHours);
date.setUTCMinutes(utcMinutes);
date.setUTCSeconds(utcSeconds);
return date;
}
function fromIntegerLiteral(literal) {
return parseInt(literal.value, 10);
}
function fromDecimalLiteral(literal) {
return parseFloat(literal.value);
}
function fromStringLiteral(literal) {
return literal.value;
}
function hasLocale(value) {
if (isLiteral(value)) {
return value.language.length > 0;
}
return false;
}
function fromLocaleStringLiteral(value) {
return {
locale: value.language,
value: value.value
};
}
function fromLiteral(literal) {
if (isDateTimeLiteral(literal)) {
return fromDateTimeLiteral(literal);
}
if (isIntegerLiteral(literal)) {
return fromIntegerLiteral(literal);
}
if (isDecimalLiteral(literal)) {
return fromDecimalLiteral(literal);
}
return literal.value;
}
function asLiteral(literal) {
if (literal instanceof Date) {
// To align with rdflib, we ignore miliseconds:
// https://github.com/linkeddata/rdflib.js/blob/d84af88f367b8b5f617c753d8241c5a2035458e8/src/literal.js#L74
var roundedDate = new Date(Date.UTC(literal.getUTCFullYear(), literal.getUTCMonth(), literal.getUTCDate(), literal.getUTCHours(), literal.getUTCMinutes(), literal.getUTCSeconds(), 0));
// Truncate the `.000Z` at the end (i.e. the miliseconds), to plain `Z`:
var rdflibStyleString = roundedDate.toISOString().replace(/\.000Z$/, 'Z');
return DataFactory.literal(rdflibStyleString, DataFactory.namedNode('http://www.w3.org/2001/XMLSchema#dateTime'));
}
if (typeof literal === 'number' && Number.isInteger(literal)) {
return DataFactory.literal(literal.toString(), DataFactory.namedNode('http://www.w3.org/2001/XMLSchema#integer'));
}
if (typeof literal === 'number' && !Number.isInteger(literal)) {
return DataFactory.literal(literal.toString(), DataFactory.namedNode('http://www.w3.org/2001/XMLSchema#decimal'));
}
return DataFactory.literal(literal.toString());
}
/**
* @internal
*/
function instantiateLocalTripleDocument(dataset, subjectCache, metadata) {
var _this = this;
var bareTripleDocument = instantiateBareTripleDocument(subjectCache, metadata);
var asRef = function () { return metadata.documentRef; };
var save = function (subjects) {
if (subjects === void 0) { subjects = Object.values(subjectCache.getAccessedSubjects()); }
return __awaiter(_this, void 0, void 0, function () {
var pendingChanges, updatedMetadata, response, message, aclRef, webSocketRef;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
pendingChanges = getPendingChanges(subjects, tripleDocumentWithRef, dataset);
return [4 /*yield*/, create(metadata.documentRef, pendingChanges.allAdditions)];
case 1:
response = _a.sent();
if (!!response.ok) return [3 /*break*/, 3];
return [4 /*yield*/, response.text()];
case 2:
message = _a.sent();
throw new Error(message);
case 3:
updatedMetadata = __assign(__assign({}, metadata), { existsOnPod: true });
aclRef = extractAclRef(response, metadata.documentRef);
if (aclRef) {
updatedMetadata.aclRef = aclRef;
}
webSocketRef = response.headers.get('Updates-Via');
if (webSocketRef) {
updatedMetadata.webSocketRef = webSocketRef;
}
// Instantiate a new TripleDocument that includes the updated Triples:
return [2 /*return*/, instantiateDocument(pendingChanges.newTriples, updatedMetadata)];
}
});
});
};
var tripleDocumentWithRef = __assign(__assign({}, bareTripleDocument), { save: save, asRef: asRef,
// Deprecated alias:
asNodeRef: asRef });
// Make sure that when TripleSubjects get initialised for this Document,
// they're attached to the Document instance that includes its Reference:
subjectCache.setDocument(tripleDocumentWithRef);
return tripleDocumentWithRef;
}
/**
* @internal
*/
function instantiateFullTripleDocument(dataset, subjectCache, metadata) {
var _this = this;
var tripleDocumentWithRef = instantiateLocalTripleDocument(dataset, subjectCache, metadata);
var getAclRef = function () {
return metadata.aclRef || null;
};
var getWebSocketRef = function () {
return metadata.webSocketRef || null;
};
var removeSubject = function (subjectRef) {
var subject = subjectCache.getSubject(subjectRef);
return subject.clear();
};
var findSubject = function (predicateRef, objectRef) {
var findSubjectRef = withDocumentSingular(findSubjectInDataset, dataset);
var subjectRef = findSubjectRef(predicateRef, objectRef);
if (!subjectRef || !isReference(subjectRef)) {
return null;
}
return subjectCache.getSubject(subjectRef);
};
var findSubjects = function (predicateRef, objectRef) {
var findSubjectRefs = withDocumentPlural(findSubjectsInDataset, dataset);
var subjectRefs = findSubjectRefs(predicateRef, objectRef);
return subjectRefs.filter(isReference).map(subjectCache.getSubject);
};
var getAllSubjects = function () {
var allSubjectRefsInTriples = findEntitiesInDataset(dataset, 'subject', null, null, null)
.filter(isReference);
var uniqueSubjectRefs = Array.from(new Set(allSubjectRefsInTriples));
return uniqueSubjectRefs.map(function (subjectRef) { return subjectCache.getSubject(subjectRef); });
};
var getAllSubjectsOfType = function (typeRef) {
return findSubjects('http://www.w3.org/1999/02/22-rdf-syntax-ns#type', typeRef);
};
var save = function (subjects) {
if (subjects === void 0) { subjects = Object.values(subjectCache.getAccessedSubjects()); }
return __awaiter(_this, void 0, void 0, function () {
var pendingChanges, updatedMetadata, response, message;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
pendingChanges = getPendingChanges(subjects, tripleDocument, dataset);
return [4 /*yield*/, update(metadata.documentRef, pendingChanges.allDeletions, pendingChanges.allAdditions)];
case 1:
response = _a.sent();
if (!!response.ok) return [3 /*break*/, 3];
return [4 /*yield*/, response.text()];
case 2:
message = _a.sent();
throw new Error(message);
case 3:
updatedMetadata = __assign(__assign({}, metadata), { existsOnPod: true });
// Instantiate a new TripleDocument that includes the updated Triples:
return [2 /*return*/, instantiateDocument(pendingChanges.newTriples, updatedMetadata)];
}
});
});
};
var getStore = function () { return dataset; };
var getTriples = function () { return dataset.toArray(); };
var tripleDocument = __assign(__assign({}, tripleDocumentWithRef), { save: save, removeSubject: removeSubject, getSubject: subjectCache.getSubject, getAllSubjectsOfType: getAllSubjectsOfType, findSubject: findSubject, findSubjects: findSubjects, getAclRef: getAclRef, getWebSocketRef: getWebSocketRef,
// Experimental methods:
experimental_getAllSubjects: getAllSubjects,
// Escape hatches, should not be necessary:
getStore: getStore, getTriples: getTriples,
// Deprecated aliases, included for backwards compatibility:
getAcl: getAclRef, getStatements: getTriples, getSubjectsOfType: getAllSubjectsOfType });
// Make sure that when TripleSubjects get initialised for this Document,
// they're attached to the fully initialised Document instance:
subjectCache.setDocument(tripleDocument);
return tripleDocument;
}
var withDocumentSingular = function (getEntityFromTriples, dataset) {
return function (knownEntity1, knownEntity2) {
return getEntityFromTriples(dataset, knownEntity1, knownEntity2);
};
};
var withDocumentPlural = function (getEntitiesFromTriples, dataset) {
return function (knownEntity1, knownEntity2) {
return getEntitiesFromTriples(dataset, knownEntity1, knownEntity2);
};
};
/**
* @internal
*/
function instantiateLocalTripleDocumentForContainer(dataset, subjectCache, metadata) {
var _this = this;
var bareTripleDocument = instantiateBareTripleDocument(subjectCache, metadata);
var save = function (subjects) {
if (subjects === void 0) { subjects = Object.values(subjectCache.getAccessedSubjects()); }
return __awaiter(_this, void 0, void 0, function () {
var pendingChanges, updatedMetadata, containerResponse, locationHeader, message, documentRef, documentResponse, aclRef, webSocketRef;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
pendingChanges = getPendingChanges(subjects, localTripleDocumentForContainer, dataset);
return [4 /*yield*/, createInContainer(metadata.containerRef, pendingChanges.allAdditions)];
case 1:
containerResponse = _a.sent();
locationHeader = containerResponse.headers.get('Location');
if (!(!containerResponse.ok || locationHeader === null)) return [3 /*break*/, 3];
return [4 /*yield*/, containerResponse.text()];
case 2:
message = _a.sent();
throw new Error(message);
case 3:
documentRef = new URL(locationHeader, new URL(metadata.containerRef).origin).href;
updatedMetadata = __assign(__assign({}, metadata), { containerRef: undefined, documentRef: documentRef, existsOnPod: true });
return [4 /*yield*/, head(documentRef)];
case 4:
documentResponse = _a.sent();
aclRef = extractAclRef(documentResponse, documentRef);
if (aclRef) {
updatedMetadata.aclRef = aclRef;
}
webSocketRef = documentResponse.headers.get('Updates-Via');
if (webSocketRef) {
updatedMetadata.webSocketRef = webSocketRef;
}
// Instantiate a new TripleDocument that includes the updated Triples:
return [2 /*return*/, instantiateDocument(pendingChanges.newTriples, updatedMetadata)];
}
});
});
};
var localTripleDocumentForContainer = __assign(__assign({}, bareTripleDocument), { save: save });
// Make sure that when TripleSubjects get initialised for this Document,
// they're attached to this Document instance:
subjectCache.setDocument(localTripleDocumentForContainer);
return localTripleDocumentForContainer;
}
/**
* @ignore Not yet a supported API.
*/
function hasRef(document) {
return typeof document.asRef === 'function';
}
/**
* @ignore Not yet a supported API.
*/
function isSavedToPod(document) {
return typeof document.getTriples === 'function';
}
/**
* Initialise a new Turtle document
*
* Note that this Document will not be created on the Pod until you call [[save]] on it.
*
* @param ref URL where this document should live
*/
function createDocument(ref) {
return instantiateDocument([], { documentRef: ref, existsOnPod: false });
}
/**
* Initialise a new Turtle Document in a Container
*
* Note that this Document will not be created on the Pod until you call [[save]] on it.
*
* @param containerRef URL of the Container in which this document should live
*/
function createDocumentInContainer(containerRef) {
return instantiateDocument([], { containerRef: containerRef, existsOnPod: false });
}
/**
* Retrieve a document containing RDF triples
*
* @param documentRef Where the document lives.
* @returns Representation of triples in the document at `uri`.
*/
function fetchDocument(uri) {
return __awaiter(this, void 0, void 0, function () {
var docUrl, documentRef, response, rawDocument, triples, aclRef, webSocketRef;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
docUrl = new URL(uri);
documentRef = docUrl.origin + docUrl.pathname + docUrl.search;
return [4 /*yield*/, get(documentRef)];
case 1:
response = _a.sent();
if (response.ok === false) {
throw new Error("Fetching the Document failed: " + response.status + " " + response.statusText + ".");
}
return [4 /*yield*/, response.text()];
case 2:
rawDocument = _a.sent();
return [4 /*yield*/, turtleToTriples(rawDocument, documentRef)];
case 3:
triples = _a.sent();
aclRef = extractAclRef(response, documentRef);
webSocketRef = response.headers.get('Updates-Via');
return [2 /*return*/, instantiateDocument(triples, {
aclRef: aclRef,
documentRef: documentRef,
webSocketRef: webSocketRef || undefined,
existsOnPod: true,
})];
}
});
});
}
/**
* @internal
*/
function extractAclRef(response, documentRef) {
var aclRef;
var linkHeader = response.headers.get('Link');
// `LinkHeader` might not be present when using the UMD build in the browser,
// in which case we just don't parse the ACL header. It is recommended to use a non-UMD build
// that supports code splitting anyway.
if (linkHeader && LinkHeader) {
var parsedLinks = LinkHeader.parse(linkHeader);
var aclLinks = parsedLinks.get('rel', 'acl');
if (aclLinks.length === 1) {
aclRef = new URL(aclLinks[0].uri, documentRef).href;
}
}
return aclRef;
}
function hasKnownRef(metadata) {
return typeof metadata.documentRef === 'string';
}
function existsOnPod(metadata) {
return metadata.existsOnPod === true;
}
function instantiateDocument(triples, metadata) {
var dataset = initialiseDataset();
dataset.addAll(triples);
var subjectCache = initialiseSubjectCache();
if (!hasKnownRef(metadata)) {
return instantiateLocalTripleDocumentForContainer(dataset, subjectCache, metadata);
}
if (!existsOnPod(metadata)) {
return instantiateLocalTripleDocument(dataset, subjectCache, metadata);
}
return instantiateFullTripleDocument(dataset, subjectCache, metadata);
}
function initialiseSubjectCache() {
var sourceDocument;
var accessedSubjects = {};
var setDocument = function (newDocument) {
sourceDocument = newDocument;
};
var getSubject = function (subjectRef) {
// Allow relative URLs to access Subjects if we know where the Document is:
subjectRef = hasRef(sourceDocument)
? new URL(subjectRef, sourceDocument.asRef()).href
: subjectRef;
if (!accessedSubjects[subjectRef]) {
accessedSubjects[subjectRef] = initialiseSubject(sourceDocument, subjectRef);
}
return accessedSubjects[subjectRef];
};
var getAccessedSubjects = function () { return accessedSubjects; };
return {
getSubject: getSubject,
setDocument: setDocument,
getAccessedSubjects: getAccessedSubjects,
};
}
/**
* @internal
*/
function instantiateBareTripleDocument(subjectCache, metadata) {
var addSubject = function (_a) {
var _b = _a === void 0 ? {} : _a, _c = _b.identifier, identifier = _c === void 0 ? generateIdentifier() : _c, _d = _b.identifierPrefix, identifierPrefix = _d === void 0 ? '' : _d;
var subjectRef = (hasKnownRef(metadata) ? metadata.documentRef : '') + '#' + identifierPrefix + identifier;
return subjectCache.getSubject(subjectRef);
};
var bareTripleDocument = {
addSubject: addSubject,
};
return bareTripleDocument;
}
/**
* @internal
*/
function getPendingChanges(subjects, document, dataset) {
var relevantSubjects = subjects.filter(function (subject) { return subject.getDocument() === document; });
var _a = relevantSubjects.reduce(function (_a, subject) {
var deletionsSoFar = _a[0], additionsSoFar = _a[1];
var _b = subject.getPendingTriples(), deletions = _b[0], additions = _b[1];
return [deletionsSoFar.concat(deletions), additionsSoFar.concat(additions)];
}, [[], []]), allDeletions = _a[0], allAdditions = _a[1];
var newTriples = dataset.toArray()
.concat(allAdditions)
.filter(function (tripleToDelete) { return allDeletions.findIndex(function (triple) { return triple.equals(tripleToDelete); }) === -1; });
return {
allAdditions: allAdditions,
allDeletions: allDeletions,
newTriples: newTriples,
};
}
/**
* Generate a string that can be used as the unique identifier for a Subject
*
* This function work