lincd
Version:
LINCD is a JavaScript library for building user interfaces with linked data (also known as 'structured data', or RDF)
392 lines • 19.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.initTree = exports.linkedPackage = exports.autoLoadOntologyData = exports.setDefaultPageLimit = exports.DEFAULT_LIMIT = void 0;
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
const models_js_1 = require("../models.js");
const SHACL_js_1 = require("../shapes/SHACL.js");
const Shape_js_1 = require("../shapes/Shape.js");
const Prefix_js_1 = require("./Prefix.js");
const CoreSet_js_1 = require("../collections/CoreSet.js");
const lincd_js_1 = require("../ontologies/lincd.js");
const npm_js_1 = require("../ontologies/npm.js");
const rdf_js_1 = require("../ontologies/rdf.js");
const URI_js_1 = require("./URI.js");
const ShapeClass_js_1 = require("./ShapeClass.js");
const LinkedComponent_js_1 = require("../utils/LinkedComponent.js");
const shacl_js_1 = require("../ontologies/shacl.js");
const rdfs_js_1 = require("../ontologies/rdfs.js");
const xsd_js_1 = require("../ontologies/xsd.js");
const SHACL_js_2 = require("../shapes/SHACL.js");
// var packageParsePromises: Map<string,Promise<any>> = new Map();
// var loadedPackages: Set<NamedNode> = new Set();
let shapeToComponents = new Map();
let ontologies = new Set();
let _autoLoadOntologyData = false;
/**
* Convert some node to a prefixed format:
* - http://some-example.org/prop > ex:prop
* */
const prefix = (n) => Prefix_js_1.Prefix.toPrefixed(n.uri);
exports.DEFAULT_LIMIT = 12;
function setDefaultPageLimit(limit) {
exports.DEFAULT_LIMIT = limit;
}
exports.setDefaultPageLimit = setDefaultPageLimit;
function autoLoadOntologyData(value) {
_autoLoadOntologyData = value;
//this may be set to true after some ontologies have already indexed,
if (_autoLoadOntologyData) {
// so in that case we load all data of ontologies that are already indexed
ontologies.forEach((ontologyExport) => {
//see linkedOntology() where we store the data loading method under the _load key
if (ontologyExport['_load']) {
ontologyExport['_load']();
}
});
}
}
exports.autoLoadOntologyData = autoLoadOntologyData;
function linkedPackage(packageName) {
let packageNode = models_js_1.NamedNode.getOrCreate(`${SHACL_js_1.LINCD_DATA_ROOT}module/${packageName}`, true);
let packageNameURI = URI_js_1.URI.sanitize(packageName);
//set certain values but don't emit change events or alteration events
new models_js_1.Quad(packageNode, rdf_js_1.rdf.type, lincd_js_1.lincd.Module, models_js_1.defaultGraph, false, false, false);
new models_js_1.Quad(packageNode, npm_js_1.npm.packageName, new models_js_1.Literal(packageName), models_js_1.defaultGraph, false, false, false);
let packageTreeObject = registerPackageInTree(packageName);
//#Create declarators for this module
let registerPackageExport = function (object) {
if (object.name in packageTreeObject) {
console.warn(`Key ${object.name} was already defined for package ${packageName}. Note that LINCD currently only supports unique names across your entire package. Overwriting ${object.name} with new value`);
}
packageTreeObject[object.name] = object;
};
let registerInPackageTree = function (exportName, exportedObject) {
packageTreeObject[exportName] = exportedObject;
};
function registerPackageModule(_module) {
for (var key in _module.exports) {
//if the exported object itself (usually FunctionalComponents) is not named or its name is _wrappedComponent (which ends up happening in the linkedComponent method above)
//then we give it the same name as it's export name.
if (!_module.exports[key].name ||
_module.exports[key].name === '_wrappedComponent') {
Object.defineProperty(_module.exports[key], 'name', { value: key });
//manual 'hack' to set the name of the original function
if (_module.exports[key]['original'] &&
!_module.exports[key]['original']['name']) {
Object.defineProperty(_module.exports[key]['original'], 'name', {
value: key + '_implementation',
});
}
}
registerInPackageTree(key, _module.exports[key]);
}
}
//create a declarator function which Components of this module can use register themselves and add themselves to the global tree
let linkedUtil = function (constructor) {
//add the component class of this module to the global tree
registerPackageExport(constructor);
//return the original class without modifications
return constructor;
};
//method to create a linked functional component
const linkedComponent = (0, LinkedComponent_js_1.createLinkedComponentFn)(registerPackageExport, registerComponent);
const linkedSetComponent = (0, LinkedComponent_js_1.createLinkedSetComponentFn)(registerPackageExport, registerComponent);
// helper that contains the previous body; applies the decorator work to a given constructor
function applyLinkedShape(constructor, options) {
if (!constructor) {
throw new Error('Constructor is undefined, skipping registration: ' + (constructor === null || constructor === void 0 ? void 0 : constructor.toString().substring(0, 100)) + ' ' + JSON.stringify(options));
return;
}
// add the component class of this module to the global tree
registerPackageExport(constructor);
// register the component and its shape
Shape_js_1.Shape.registerByType(constructor);
// if no shape object has been attached to the constructor
if (!Object.getOwnPropertyNames(constructor).includes('shape')) {
// create a new node shape for this shapeClass
let nodeShape = SHACL_js_1.NodeShape.getFromURI((0, SHACL_js_1.getNodeShapeUri)(packageName, constructor.name));
//small fix, for some reason sometimes a node with this URI already exists but is not a NodeShape
if (!nodeShape.type) {
nodeShape.type = shacl_js_1.shacl.NodeShape;
}
// connect the typescript class to its NodeShape
constructor.shape = nodeShape;
// set the name
nodeShape.label = constructor.name;
if (options) {
if (options.description) {
nodeShape.description = options.description;
}
}
// also keep track of the reverse: nodeShape to typescript class
(0, ShapeClass_js_1.addNodeShapeToShapeClass)(nodeShape, constructor);
// also create a representation in the graph of the shape class itself
let shapeClass = models_js_1.NamedNode.getOrCreate((0, SHACL_js_1.getNodeShapeUri)(packageName, constructor.name), true);
shapeClass.set(lincd_js_1.lincd.definesShape, nodeShape.node);
shapeClass.set(rdf_js_1.rdf.type, lincd_js_1.lincd.ShapeClass);
// and connect it back to the module
shapeClass.set(lincd_js_1.lincd.module, packageNode);
//track what extends what (both on nodeShape level and shapeClass level)
const extendingShapeClass = Object.getPrototypeOf(constructor);
const extendingShape = extendingShapeClass.shape;
//if this shape class is extending something other then Shape
if (extendingShape && !(extendingShapeClass === Shape_js_1.Shape)) {
//store which nodeShape this nodeShape extends
nodeShape.extends = extendingShape;
//store which shapeClass this shapeClass extends
const extendingShapeClassNode = extendingShape.getOneInverse(lincd_js_1.lincd.definesShape);
if (extendingShapeClassNode) {
shapeClass.set(lincd_js_1.lincd.isExtending, extendingShapeClassNode);
}
}
// run deferred callbacks from property decorators
if (constructor['shapeCallbacks']) {
constructor['shapeCallbacks'].forEach((callback) => {
callback(nodeShape);
});
const nodeCallbacks = (0, SHACL_js_1.getAndClearCallbacks)(nodeShape.namedNode);
if (nodeCallbacks) {
nodeCallbacks.forEach((callback) => {
callback(nodeShape);
});
}
delete constructor['shapeCallbacks'];
}
}
else {
console.warn('This ShapeClass already has a shape: ', constructor.shape);
}
if (constructor.targetClass) {
constructor.shape.targetClass = constructor.targetClass;
}
// return the original class without modifications
// return constructor;
}
function linkedShape(arg) {
// usage as @linkedShape
if (typeof arg === 'function') {
applyLinkedShape(arg);
return;
}
// usage as @linkedShape({...}) or @linkedShape()
const options = arg;
return function (constructor) {
applyLinkedShape(constructor, options);
};
}
/**
*
* @param exports all exports of the file, simply provide "this" as value!
* @param dataSource the path leading to the ontology's data file
* @param nameSpace the base URI of the ontology
* @param prefixAndFileName the file name MUST match the prefix for this ontology
*/
let linkedOntology = function (exports, nameSpace, prefixAndFileName, loadData, dataSource) {
let exportsCopy = Object.assign({}, exports);
//store specifics in exports. And make sure we can detect this as an ontology later
exportsCopy['_ns'] = nameSpace;
exportsCopy['_prefix'] = prefixAndFileName;
exportsCopy['_load'] = loadData;
exportsCopy['_data'] = dataSource;
//register the prefix here (so just calling linkedOntology with a prefix will automatically register that prefix)
if (prefixAndFileName) {
//run the namespace without any term name, this will give back a named node with just the namespace as URI, then get that URI to provide it as full URI
Prefix_js_1.Prefix.add(prefixAndFileName, nameSpace('').uri);
}
ontologies.add(exportsCopy);
//register all the exports under the prefix. NOTE: this means the file name HAS to match the prefix
registerInPackageTree(prefixAndFileName, exportsCopy);
// });
if (_autoLoadOntologyData) {
loadData().catch((err) => {
console.warn('Could not load ontology data. Do you need to rebuild the module of the ' +
prefixAndFileName +
' ontology?', err);
});
}
};
/**
* This method is used to get a shape class in this package by its name.
* This can be used to avoid circular dependencies between shapes.
* @param name
*/
let getPackageShape = (name) => {
//get the named node of the node shape first,
//then get the shape class that defines this node shape
return (0, ShapeClass_js_1.getShapeClass)(models_js_1.NamedNode.getOrCreate((0, SHACL_js_1.getNodeShapeUri)(packageName, name)));
};
//return the declarators so the module can use them
return {
linkedComponent,
linkedSetComponent,
linkedShape,
linkedUtil,
linkedOntology,
registerPackageExport,
registerPackageModule,
getPackageShape,
packageExports: packageTreeObject,
packageName: packageName,
};
}
exports.linkedPackage = linkedPackage;
function registerComponent(exportedComponent, shape) {
if (!shape) {
//warn developers against a common mistake: if no static shape is set by the Component it will inherit the one of the class it extends
if (!exportedComponent.hasOwnProperty('shape')) {
console.warn(`Component ${exportedComponent.displayName || exportedComponent.name} is not linked to a shape.`);
return;
}
shape = exportedComponent.shape;
}
if (!shapeToComponents.has(shape)) {
shapeToComponents.set(shape, new CoreSet_js_1.CoreSet());
}
shapeToComponents.get(shape).add(exportedComponent);
}
function registerPackageInTree(packageName, packageExports) {
//prepare name for global tree reference
// let packageTreeKey = packageName.replace(/-/g,'_');
//if something with this name already registered in the global tree
if (packageName in lincd._modules) {
//This probably means package.ts is loaded twice, through different paths and could point to a problem
//So we log about it. But there is one exception. LINCD itself registers itself twice: once in the bottom of this file and once in its package.ts file.
//But if there are already other packages registered, then probably there is 2 versions of LINCD being loaded, and that IS a problem.
if (packageName !== 'lincd' || Object.keys(lincd._modules).length !== 1) {
console.warn('A package with the name ' +
packageName +
' has already been registered. Adding to existing object');
}
Object.assign(lincd._modules[packageName], packageExports);
}
else {
//initiate an empty object for this module in the global tree
lincd._modules[packageName] = packageExports || {};
}
return lincd._modules[packageName];
}
function initTree() {
let globalObject = typeof window !== 'undefined'
? window
: typeof global !== 'undefined'
? global
: undefined;
if ('lincd' in globalObject) {
throw new Error('Multiple versions of LINCD are loaded');
}
else {
globalObject['lincd'] = { _modules: {} };
}
}
exports.initTree = initTree;
//when this file is used, make sure the tree is initialized
initTree();
//now that this file is set up, we can link linked shapes in the LINCD module itself
let lincdPackage = linkedPackage('lincd');
lincdPackage.linkedShape({
description: 'Represents a SHACL NodeShape; defines constraints for a class of RDF nodes. Links to multiple PropertyShapes. (schema, constraint, class validation)',
})(SHACL_js_1.NodeShape);
lincdPackage.linkedShape({
description: 'Represents a SHACL PropertyShape; specifies rules for one property of a NodeShape (path, datatype, cardinality). (validation rule, property constraint)',
})(SHACL_js_1.PropertyShape);
lincdPackage.linkedShape({
description: 'ValidationReport produced by a SHACL engine; summarizes results of validating data against NodeShapes and PropertyShapes. (report, conformance, summary)',
})(SHACL_js_1.ValidationReport);
lincdPackage.linkedShape({
description: 'Individual result entry in a ValidationReport; details a specific violation or success, pointing to the node, property, and constraint. (error, issue, finding)',
})(SHACL_js_1.ValidationResult);
//ALL the following is to support Shape having get/set methods with property shapes
//and Shape itself having a nodeShape
//if we dont need Shape to have get/set methods (like label and type) then this can be removed
Shape_js_1.Shape.shape = SHACL_js_1.NodeShape.getFromURI('https://data.lincd.org/module/lincd/shape/shape');
(0, ShapeClass_js_1.addNodeShapeToShapeClass)(Shape_js_1.Shape.shape, Shape_js_1.Shape);
//Here we can register the properties of the Shape class itself
//We can't do that inside of Shape because it would cause circular dependencies
(0, SHACL_js_2.createPropertyShape)({
path: rdfs_js_1.rdfs.label,
//TODO: multiple labels should be possible
maxCount: 1, //currently get label is implemented to return a single value
}, 'label', shacl_js_1.shacl.Literal, Shape_js_1.Shape);
(0, SHACL_js_2.createPropertyShape)({
path: rdf_js_1.rdf.type,
shape: Shape_js_1.Shape,
}, 'type', shacl_js_1.shacl.IRI, Shape_js_1.Shape);
(0, SHACL_js_2.createPropertyShape)({
path: shacl_js_1.shacl.property,
shape: SHACL_js_1.PropertyShape,
}, 'properties', shacl_js_1.shacl.IRI, SHACL_js_1.NodeShape);
(0, SHACL_js_2.createPropertyShape)({
path: rdfs_js_1.rdfs.comment,
maxCount: 1,
}, 'description', shacl_js_1.shacl.Literal, SHACL_js_1.NodeShape);
(0, SHACL_js_2.createPropertyShape)({
path: rdf_js_1.rdf.type,
maxCount: 1,
shape: Shape_js_1.Shape,
}, 'type', shacl_js_1.shacl.IRI, SHACL_js_1.NodeShape);
(0, SHACL_js_2.createPropertyShape)({
path: shacl_js_1.shacl.targetClass,
shape: Shape_js_1.Shape, //should be rdfs Class, but that's currently not available in LINCD. So queries currently cannot continue after accessing targetClass
maxCount: 1,
}, 'targetClass', shacl_js_1.shacl.IRI, SHACL_js_1.NodeShape);
(0, SHACL_js_2.createPropertyShape)({
path: shacl_js_1.shacl.description,
maxCount: 1,
}, 'type', shacl_js_1.shacl.Literal, SHACL_js_1.NodeShape);
(0, SHACL_js_2.createPropertyShape)({
path: shacl_js_1.shacl.targetNode,
shape: Shape_js_1.Shape, //actually returns a NamedNode... is this correct then? Should we define or use a rdfs Class that matches the potential values?
}, 'targetNode', shacl_js_1.shacl.IRI, SHACL_js_1.NodeShape);
(0, SHACL_js_2.createPropertyShape)({
path: lincd_js_1.lincd.isExtending,
shape: SHACL_js_1.NodeShape,
}, 'extends', shacl_js_1.shacl.IRI, SHACL_js_1.NodeShape);
//currently path accepts multiple values, so its a multi-value property
//these values will be consequent properties that follow each other. Other property paths are not supported yet.
(0, SHACL_js_2.createPropertyShape)({
path: shacl_js_1.shacl.path,
shape: Shape_js_1.Shape,
}, 'path', shacl_js_1.shacl.IRI, SHACL_js_1.PropertyShape);
(0, SHACL_js_2.createPropertyShape)({
path: shacl_js_1.shacl.node,
shape: SHACL_js_1.NodeShape,
maxCount: 1,
}, 'valueShape', shacl_js_1.shacl.IRI, SHACL_js_1.PropertyShape);
(0, SHACL_js_2.createPropertyShape)({
maxCount: 1,
path: shacl_js_1.shacl.nodeKind,
shape: Shape_js_1.Shape, //actually returns a NamedNode. Queries currently cannot continue after accessing nodeKind
}, 'nodeKind', shacl_js_1.shacl.IRI, SHACL_js_1.PropertyShape);
(0, SHACL_js_2.createPropertyShape)({
path: shacl_js_1.shacl.datatype,
shape: Shape_js_1.Shape,
maxCount: 1,
}, 'datatype', shacl_js_1.shacl.IRI, SHACL_js_1.PropertyShape);
//PropertyShape.maxCount
(0, SHACL_js_2.createPropertyShape)({
path: shacl_js_1.shacl.maxCount,
datatype: xsd_js_1.xsd.integer,
maxCount: 1,
}, 'maxCount', shacl_js_1.shacl.Literal, SHACL_js_1.PropertyShape);
//PropertyShape.minCount
(0, SHACL_js_2.createPropertyShape)({
path: shacl_js_1.shacl.minCount,
datatype: xsd_js_1.xsd.integer,
maxCount: 1,
}, 'minCount', shacl_js_1.shacl.Literal, SHACL_js_1.PropertyShape);
//PropertyShape.name
(0, SHACL_js_2.createPropertyShape)({
path: shacl_js_1.shacl.name,
maxCount: 1,
}, 'name', shacl_js_1.shacl.Literal, SHACL_js_1.PropertyShape);
//PropertyShape.description
(0, SHACL_js_2.createPropertyShape)({
path: shacl_js_1.shacl.description,
maxCount: 1,
}, 'description', shacl_js_1.shacl.Literal, SHACL_js_1.PropertyShape);
//PropertyShape.inList
//# sourceMappingURL=Package.js.map