@faubulous/mentor-rdf 2.1.9 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4692,7 +4692,7 @@ var ReasonerBase = class {
4692
4692
  this.store = store;
4693
4693
  this.sourceGraph = this.getGraphNode(sourceGraph);
4694
4694
  this.targetGraph = this.getGraphNode(targetGraph);
4695
- for (let quad5 of store.match(null, null, null, this.targetGraph)) {
4695
+ for (const quad5 of Array.from(store.match(null, null, null, this.targetGraph))) {
4696
4696
  store.delete(quad5);
4697
4697
  }
4698
4698
  this.beforeInference();
@@ -4842,6 +4842,18 @@ var RdfsReasoner = class extends SkosReasoner {
4842
4842
  this.classes = /* @__PURE__ */ new Set();
4843
4843
  this.properties = /* @__PURE__ */ new Set();
4844
4844
  this.individuals = /* @__PURE__ */ new Set();
4845
+ /**
4846
+ * The super properties of each property, as asserted with `rdfs:subPropertyOf`.
4847
+ */
4848
+ this._superProperties = /* @__PURE__ */ new Map();
4849
+ /**
4850
+ * The ranges asserted for each property.
4851
+ */
4852
+ this._ranges = /* @__PURE__ */ new Map();
4853
+ /**
4854
+ * The domains asserted for each property.
4855
+ */
4856
+ this._domains = /* @__PURE__ */ new Map();
4845
4857
  }
4846
4858
  isIgnoredNode(term) {
4847
4859
  switch (term.value) {
@@ -4866,6 +4878,47 @@ var RdfsReasoner = class extends SkosReasoner {
4866
4878
  for (let individual of individuals) {
4867
4879
  this.store.add(quad2(individual, rdf.type, owl.NamedIndividual, this.targetGraph));
4868
4880
  }
4881
+ this._inferInheritedRangesAndDomains();
4882
+ }
4883
+ /**
4884
+ * Infers the ranges and domains that properties inherit from their super properties
4885
+ * (OWL 2 RL rules scm-rng2 and scm-dom2), following `rdfs:subPropertyOf` chains.
4886
+ */
4887
+ _inferInheritedRangesAndDomains() {
4888
+ for (const { property, superProperties } of this._superProperties.values()) {
4889
+ for (const superProperty of this._getAncestorProperties(property.value, superProperties)) {
4890
+ for (const range of this._ranges.get(superProperty) ?? []) {
4891
+ this.store.add(quad2(property, rdfs.range, range, this.targetGraph));
4892
+ }
4893
+ for (const domain of this._domains.get(superProperty) ?? []) {
4894
+ this.store.add(quad2(property, rdfs.domain, domain, this.targetGraph));
4895
+ }
4896
+ }
4897
+ }
4898
+ }
4899
+ /**
4900
+ * Gets the transitive super properties of a property, nearest first. Cycles in the
4901
+ * property hierarchy are followed only once.
4902
+ * @param property The IRI or blank node id of a property.
4903
+ * @param superProperties The direct super properties of the property.
4904
+ * @returns The IRIs of all super properties, excluding the property itself.
4905
+ */
4906
+ _getAncestorProperties(property, superProperties) {
4907
+ const result = [];
4908
+ const visited = /* @__PURE__ */ new Set([property]);
4909
+ const queue = [...superProperties];
4910
+ while (queue.length > 0) {
4911
+ const current = queue.shift();
4912
+ if (visited.has(current)) {
4913
+ continue;
4914
+ }
4915
+ visited.add(current);
4916
+ result.push(current);
4917
+ for (const next of this._superProperties.get(current)?.superProperties ?? []) {
4918
+ queue.push(next);
4919
+ }
4920
+ }
4921
+ return result;
4869
4922
  }
4870
4923
  resetState() {
4871
4924
  super.resetState();
@@ -4873,6 +4926,9 @@ var RdfsReasoner = class extends SkosReasoner {
4873
4926
  this.classes.clear();
4874
4927
  this.properties.clear();
4875
4928
  this.individuals.clear();
4929
+ this._superProperties.clear();
4930
+ this._ranges.clear();
4931
+ this._domains.clear();
4876
4932
  }
4877
4933
  applyInference(quad5) {
4878
4934
  super.applyInference(quad5);
@@ -4966,13 +5022,49 @@ var RdfsReasoner = class extends SkosReasoner {
4966
5022
  }
4967
5023
  return;
4968
5024
  }
4969
- case rdfs.range.value:
5025
+ case rdfs.subPropertyOf.value: {
5026
+ this._addSuperProperty(s, o);
5027
+ return;
5028
+ }
5029
+ case rdfs.range.value: {
5030
+ this.assertProperty(s);
5031
+ this._addToList(this._ranges, s.value, o);
5032
+ return;
5033
+ }
4970
5034
  case rdfs.domain.value: {
4971
5035
  this.assertProperty(s);
5036
+ this._addToList(this._domains, s.value, o);
4972
5037
  return;
4973
5038
  }
4974
5039
  }
4975
5040
  }
5041
+ /**
5042
+ * Records that a property is a sub property of another property.
5043
+ * @param property The sub property.
5044
+ * @param superProperty The super property.
5045
+ */
5046
+ _addSuperProperty(property, superProperty) {
5047
+ let entry = this._superProperties.get(property.value);
5048
+ if (!entry) {
5049
+ entry = { property, superProperties: /* @__PURE__ */ new Set() };
5050
+ this._superProperties.set(property.value, entry);
5051
+ }
5052
+ entry.superProperties.add(superProperty.value);
5053
+ }
5054
+ /**
5055
+ * Adds a value to the list stored under a key.
5056
+ * @param map The map of lists.
5057
+ * @param key The key of the list.
5058
+ * @param value The value to add.
5059
+ */
5060
+ _addToList(map, key, value) {
5061
+ const list = map.get(key);
5062
+ if (list) {
5063
+ list.push(value);
5064
+ } else {
5065
+ map.set(key, [value]);
5066
+ }
5067
+ }
4976
5068
  };
4977
5069
 
4978
5070
  // src/rdf/reasoners/shacl-reasoner.ts
@@ -9319,7 +9411,7 @@ var toRdflibTerm = (term) => {
9319
9411
  };
9320
9412
 
9321
9413
  // src/rdf/store.ts
9322
- import { TurtleLexer, TurtleParser, TurtleReader, NQuadsLexer, NQuadsParser, NQuadsReader, createFileBlankNodeIdGenerator } from "@faubulous/mentor-rdf-parsers";
9414
+ import { TurtleLexer, TurtleParser, TurtleReader, TurtleQuadReader, QuadReaderError, NQuadsLexer, NQuadsParser, NQuadsReader, createFileBlankNodeIdGenerator } from "@faubulous/mentor-rdf-parsers";
9323
9415
  var TripleNotFoundError = class extends Error {
9324
9416
  /**
9325
9417
  * Create a new instance of the error.
@@ -9337,14 +9429,17 @@ var Store = class {
9337
9429
  */
9338
9430
  constructor(reasoner) {
9339
9431
  /**
9340
- * The adapted RDF.js triple store implementation.
9341
- *
9342
- * Note: Please do not use this directly, but rather use the `_ds` property to access the dataset.
9432
+ * The adapted RDF.js triple store implementation. Replaced by {@link clear}.
9433
+ *
9434
+ * Reads go through `_store.readQuads`, a live iterator over the indexes. The dataset
9435
+ * view's `match` is never used for reading: rdf-stores implements it by copying every
9436
+ * matching quad into a new store first, which costs hundreds of megabytes and
9437
+ * seconds for a graph the size of a workspace vocabulary.
9343
9438
  */
9344
9439
  this._store = RdfStore2.createDefault();
9345
9440
  /**
9346
- * The RDF dataset containing the triples in the store, which should primarily be used
9347
- * for any operations so that the store can be swapped out with a different implementation.
9441
+ * The RDF dataset view of {@link _store}, used for the mutations and membership
9442
+ * checks of the `DatasetCore` interface.
9348
9443
  */
9349
9444
  this._ds = this._store.asDataset();
9350
9445
  /**
@@ -9437,13 +9532,29 @@ var Store = class {
9437
9532
  /**
9438
9533
  * Get the URIs of the graphs in the triple store.
9439
9534
  * @returns An array of graph URIs in no particular order.
9535
+ * @note Every mutation of the store records the graph it touched, so the graphs are the
9536
+ * recorded ones that still hold a triple. Matching all triples instead would copy the whole
9537
+ * store, which takes seconds for a store holding a large workspace.
9440
9538
  */
9441
9539
  getGraphs() {
9442
- const result = /* @__PURE__ */ new Set();
9443
- for (const q of this._ds.match(null, null, null, null)) {
9444
- result.add(q.graph.value);
9540
+ return Array.from(this._graphVersions.keys()).filter((graphUri) => this._hasTriples(graphUri));
9541
+ }
9542
+ /**
9543
+ * Indicates if a graph holds at least one triple, without reading more than one.
9544
+ * @param graph The graph term, or its URI with an empty string for the default graph.
9545
+ */
9546
+ _hasTriples(graph2) {
9547
+ return !this._store.readQuads(null, null, null, this._getGraphTerm(graph2)).next().done;
9548
+ }
9549
+ /**
9550
+ * Resolves a graph URI to its term; an empty string denotes the default graph.
9551
+ * @param graph The graph term or URI.
9552
+ */
9553
+ _getGraphTerm(graph2) {
9554
+ if (typeof graph2 !== "string") {
9555
+ return graph2;
9445
9556
  }
9446
- return Array.from(result);
9557
+ return graph2 === "" ? this.dataFactory.defaultGraph() : this.dataFactory.namedNode(graph2);
9447
9558
  }
9448
9559
  /**
9449
9560
  * Load a set of triples into the store.
@@ -9478,11 +9589,7 @@ var Store = class {
9478
9589
  _loadQuads(quads, graphUri, executeInference = true, clearGraph = true, onQuad) {
9479
9590
  const graph2 = this.dataFactory.namedNode(graphUri.replace("\\", "/"));
9480
9591
  if (clearGraph) {
9481
- this.deleteGraphs([graphUri]);
9482
- if (this.reasoner) {
9483
- const inferenceGraph = this.dataFactory.namedNode(this.reasoner.targetUriGenerator.getGraphUri(graphUri));
9484
- this.deleteGraphs([inferenceGraph.value]);
9485
- }
9592
+ this.deleteGraphs([graphUri], true);
9486
9593
  }
9487
9594
  for (let q of quads) {
9488
9595
  this._ds.add(this.dataFactory.quad(q.subject, q.predicate, q.object, graph2));
@@ -9492,10 +9599,21 @@ var Store = class {
9492
9599
  }
9493
9600
  this._touchGraph(graph2.value);
9494
9601
  if (this.reasoner && executeInference) {
9495
- this.reasoner.expand(this._ds, graphUri);
9496
- this._touchGraph(this.reasoner.targetUriGenerator.getGraphUri(graphUri));
9602
+ this._expand(graphUri);
9497
9603
  }
9498
9604
  }
9605
+ /**
9606
+ * Runs the reasoner over a graph. The reasoner is handed this store rather than the
9607
+ * dataset view, so its reads are live iterators and its writes bump the graph versions.
9608
+ * @param graphUri The URI of the source graph.
9609
+ */
9610
+ _expand(graphUri) {
9611
+ if (!this.reasoner) {
9612
+ return;
9613
+ }
9614
+ this.reasoner.expand(this, graphUri);
9615
+ this._touchGraph(this.reasoner.targetUriGenerator.getGraphUri(graphUri));
9616
+ }
9499
9617
  /**
9500
9618
  * Create an RDF store from Turtle, N3 or N-Triples data.
9501
9619
  * @param input Input string in Turtle, N3 or N-Triples format.
@@ -9507,11 +9625,29 @@ var Store = class {
9507
9625
  */
9508
9626
  loadTurtle(input, graphUri, executeInference = true, clearGraph = true, onQuad) {
9509
9627
  const tokens = new TurtleLexer(createFileBlankNodeIdGenerator(graphUri)).tokenize(input).tokens;
9510
- const cst = new TurtleParser().parse(tokens, false);
9511
- const quads = new TurtleReader().visit(cst);
9628
+ const quads = this._readTurtleQuads(tokens);
9512
9629
  this._loadQuads(quads, graphUri, executeInference, clearGraph, onQuad);
9513
9630
  return this;
9514
9631
  }
9632
+ /**
9633
+ * Reads the quads of a Turtle token stream. A valid document is read without a syntax
9634
+ * tree, which is the fast path. A document the quad reader rejects is parsed with error
9635
+ * recovery and read from its syntax tree, so that a partially valid document yields the
9636
+ * quads it did before.
9637
+ * @param tokens The tokens of the document.
9638
+ * @returns The quads of the document.
9639
+ */
9640
+ _readTurtleQuads(tokens) {
9641
+ try {
9642
+ return new TurtleQuadReader().read(tokens);
9643
+ } catch (error) {
9644
+ if (!(error instanceof QuadReaderError)) {
9645
+ throw error;
9646
+ }
9647
+ const cst = new TurtleParser().parse(tokens, false);
9648
+ return new TurtleReader().visit(cst);
9649
+ }
9650
+ }
9515
9651
  /**
9516
9652
  * Create an RDF store from N-Quads data.
9517
9653
  * @param input Input string in N-Quads format.
@@ -9572,7 +9708,7 @@ var Store = class {
9572
9708
  const targetGraph = targetGraphUri ? this.dataFactory.namedNode(targetGraphUri) : void 0;
9573
9709
  const store = graph();
9574
9710
  const g = targetGraph ? toRdflibTerm(targetGraph) : void 0;
9575
- for (const q of this._ds.match(null, null, null, sourceGraph)) {
9711
+ for (const q of this._store.readQuads(null, null, null, sourceGraph)) {
9576
9712
  const s = toRdflibTerm(q.subject);
9577
9713
  const p = toRdflibTerm(q.predicate);
9578
9714
  const o = toRdflibTerm(q.object);
@@ -9594,40 +9730,46 @@ var Store = class {
9594
9730
  * @returns `true` if the store contains triples in the graph URI, `false` otherwise.
9595
9731
  */
9596
9732
  hasGraph(graphUri) {
9597
- const uri = typeof graphUri === "string" ? this.dataFactory.namedNode(graphUri) : graphUri;
9598
- for (const _ of this._ds.match(null, null, null, uri)) {
9599
- return true;
9600
- }
9601
- return false;
9733
+ return this._hasTriples(graphUri);
9602
9734
  }
9603
9735
  /**
9604
9736
  * Apply inference to the given graph and store the triples in the associated inference graph.
9605
9737
  * @param graphUri A graph URI.
9606
9738
  */
9607
9739
  executeInference(graphUri) {
9608
- if (this.reasoner) {
9609
- this.reasoner.expand(this._ds, graphUri);
9610
- const uri = typeof graphUri === "string" ? graphUri : graphUri.value;
9611
- this._touchGraph(this.reasoner.targetUriGenerator.getGraphUri(uri));
9612
- }
9740
+ this._expand(typeof graphUri === "string" ? graphUri : graphUri.value);
9613
9741
  }
9614
9742
  /**
9615
9743
  * Delete named graphs from the store.
9616
- * @param graphUris URIs of the graphs to be deleted.
9617
- */
9618
- deleteGraphs(graphUris) {
9619
- for (const graphUri of graphUris) {
9620
- const graph2 = this.dataFactory.namedNode(graphUri);
9621
- let deleted = false;
9622
- for (const quad5 of this._ds.match(null, null, null, graph2)) {
9623
- this._ds.delete(quad5);
9624
- deleted = true;
9625
- }
9626
- if (deleted) {
9744
+ * @param graphUris URIs of the graphs to be deleted; an empty string denotes the default graph.
9745
+ * @param includeInferred Whether to also delete the inference graph of each graph, as
9746
+ * named by the reasoner's target URI generator. Ignored when the store has no reasoner.
9747
+ */
9748
+ deleteGraphs(graphUris, includeInferred = false) {
9749
+ const uris = includeInferred && this.reasoner ? graphUris.flatMap((uri) => [uri, this.reasoner.targetUriGenerator.getGraphUri(uri)]) : graphUris;
9750
+ for (const graphUri of uris) {
9751
+ const quads = this._store.getQuads(null, null, null, this._getGraphTerm(graphUri));
9752
+ for (const quad5 of quads) {
9753
+ this._store.removeQuad(quad5);
9754
+ }
9755
+ if (quads.length > 0) {
9627
9756
  this._touchGraph(graphUri);
9628
9757
  }
9629
9758
  }
9630
9759
  }
9760
+ /**
9761
+ * Removes every quad from the store and releases the memory of the underlying store,
9762
+ * including its term dictionary, which never shrinks when quads are deleted. The
9763
+ * version counters keep increasing so that cached views and validators notice the change.
9764
+ */
9765
+ clear() {
9766
+ this._store = RdfStore2.createDefault();
9767
+ this._ds = this._store.asDataset();
9768
+ for (const graphUri of this._graphVersions.keys()) {
9769
+ this._touchGraph(graphUri);
9770
+ }
9771
+ this._version++;
9772
+ }
9631
9773
  /**
9632
9774
  * Get the URIs of ordered list members in the store.
9633
9775
  * @param graphUris Optional graph URI or array of graph URIs to query.
@@ -9677,7 +9819,9 @@ var Store = class {
9677
9819
  /**
9678
9820
  * Returns a lazy, read-only dataset view of the quads matching the given pattern.
9679
9821
  * Implements the RDF/JS `DatasetCore.match` semantics: the result reflects the
9680
- * current contents of the store and does not copy any quads.
9822
+ * current contents of the store and does not copy any quads. Because the view
9823
+ * iterates the live indexes, do not add or delete quads while iterating it;
9824
+ * take a snapshot with `Array.from(...)` first.
9681
9825
  * @param subject The optional subject.
9682
9826
  * @param predicate The optional predicate.
9683
9827
  * @param object The optional object.
@@ -9694,14 +9838,15 @@ var Store = class {
9694
9838
  });
9695
9839
  }
9696
9840
  /**
9697
- * Synchronously iterate the quads matching a single pattern.
9841
+ * Synchronously iterate the quads matching a single pattern. The iterator is live:
9842
+ * do not add or delete quads while iterating; take a snapshot with `Array.from(...)` first.
9698
9843
  * @param subject The optional subject.
9699
9844
  * @param predicate The optional predicate.
9700
9845
  * @param object The optional object.
9701
9846
  * @param graph The optional graph.
9702
9847
  */
9703
9848
  *matchQuads(subject, predicate, object, graph2) {
9704
- yield* this._ds.match(subject, predicate, object, graph2);
9849
+ yield* this._store.readQuads(subject, predicate, object, graph2);
9705
9850
  }
9706
9851
  /**
9707
9852
  * Get an RDF/JS `Source` interface for the store that streams matching quads.
@@ -9715,6 +9860,8 @@ var Store = class {
9715
9860
  }
9716
9861
  /**
9717
9862
  * Query the store for triples matching the given pattern supporting multiple graphs.
9863
+ * The iterator is live: do not add or delete quads while iterating; take a snapshot
9864
+ * with `Array.from(...)` first.
9718
9865
  * @param graphUris Optional graph URI or array of graph URIs to query.
9719
9866
  * @param subject A subject URI or null to match any subject.
9720
9867
  * @param predicate A predicate URI or null to match any predicate.
@@ -9730,19 +9877,15 @@ var Store = class {
9730
9877
  if (graphUris !== void 0) {
9731
9878
  const graphs = Array.isArray(graphUris) ? graphUris : [graphUris];
9732
9879
  for (const graph2 of graphs.map((g) => this.dataFactory.namedNode(g))) {
9733
- for (const q of this._ds.match(s, p, o, graph2)) {
9734
- yield q;
9735
- }
9880
+ yield* this._store.readQuads(s, p, o, graph2);
9736
9881
  if (includeInferred !== false && this.reasoner) {
9737
9882
  const inferenceGraphUri = this.reasoner.targetUriGenerator.getGraphUri(graph2.value);
9738
9883
  const inferenceGraph = this.dataFactory.namedNode(inferenceGraphUri);
9739
- for (let q of this._ds.match(s, p, o, inferenceGraph)) {
9740
- yield q;
9741
- }
9884
+ yield* this._store.readQuads(s, p, o, inferenceGraph);
9742
9885
  }
9743
9886
  }
9744
9887
  } else {
9745
- yield* this._ds.match(s, p, o);
9888
+ yield* this._store.readQuads(s, p, o, null);
9746
9889
  }
9747
9890
  }
9748
9891
  /**