UNPKG

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
"use strict"; 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