UNPKG

tripledoc

Version:

Library to read, create and update documents on a Solid Pod

1,111 lines (1,093 loc) 55.5 kB
'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