@oml/owl 0.12.0 → 0.14.0

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.
@@ -21,10 +21,7 @@ export declare class ReasoningStore {
21
21
  graphs(modelUri: string): ModelGraphs;
22
22
  getStore(): Store;
23
23
  private resolveGraphs;
24
- private graphsFromNamespace;
25
24
  private graphsFromModelUri;
26
- private namespaceFromQuads;
27
- private ontologyIriFromNamespace;
28
25
  private requireGraphs;
29
26
  private withGraph;
30
27
  private removeGraph;
@@ -1,6 +1,5 @@
1
1
  // Copyright (c) 2026 Modelware. All rights reserved.
2
2
  import { DataFactory, Store } from 'n3';
3
- const OML_NAMESPACE = 'http://opencaesar.io/oml#namespace';
4
3
  const { namedNode, quad } = DataFactory;
5
4
  export class ReasoningStore {
6
5
  constructor() {
@@ -9,9 +8,9 @@ export class ReasoningStore {
9
8
  }
10
9
  // Load a model's quads into its named graph.
11
10
  // Retracts any existing quads for that model first.
12
- // Graph IRI is derived from the ontology namespace embedded in the quads.
11
+ // Graph IRI is derived from the model URI to keep model snapshots isolated.
13
12
  loadModel(modelUri, quads) {
14
- const graphs = this.resolveGraphs(modelUri, quads);
13
+ const graphs = this.resolveGraphs(modelUri);
15
14
  this.retractModel(modelUri);
16
15
  this.modelGraphs.set(modelUri, graphs);
17
16
  this.store.addQuads(quads.map((q) => this.withGraph(q, graphs.own)));
@@ -25,7 +24,7 @@ export class ReasoningStore {
25
24
  this.store.addQuads(quads.map((q) => this.withGraph(q, graphs.entailments)));
26
25
  }
27
26
  applyModelDelta(modelUri, quads, retracted, asserted) {
28
- const graphs = this.resolveGraphs(modelUri, quads, true);
27
+ const graphs = this.resolveGraphs(modelUri);
29
28
  this.modelGraphs.set(modelUri, graphs);
30
29
  if (retracted.length > 0) {
31
30
  this.store.removeQuads(retracted.map((q) => this.withGraph(q, graphs.own)));
@@ -48,7 +47,7 @@ export class ReasoningStore {
48
47
  // Returns the semantic difference — what was added and what was removed.
49
48
  // If the diff is empty, nothing downstream needs to run.
50
49
  diffModel(modelUri, newQuads) {
51
- const graphs = this.resolveGraphs(modelUri, newQuads, true);
50
+ const graphs = this.resolveGraphs(modelUri);
52
51
  const existing = this.store.getQuads(null, null, null, graphs.own);
53
52
  const next = newQuads.map((q) => this.withGraph(q, graphs.own));
54
53
  const existingByKey = new Map(existing.map((q) => [this.quadKey(q), q]));
@@ -84,30 +83,14 @@ export class ReasoningStore {
84
83
  getStore() {
85
84
  return this.store;
86
85
  }
87
- resolveGraphs(modelUri, quads, allowExisting = false) {
88
- const namespace = this.namespaceFromQuads(quads);
89
- if (namespace) {
90
- const graphs = this.graphsFromNamespace(namespace);
91
- this.modelGraphs.set(modelUri, graphs);
92
- return graphs;
86
+ resolveGraphs(modelUri) {
87
+ const existing = this.modelGraphs.get(modelUri);
88
+ if (existing) {
89
+ return existing;
93
90
  }
94
- if (allowExisting) {
95
- const existing = this.modelGraphs.get(modelUri);
96
- if (existing)
97
- return existing;
98
- }
99
- // Keep reasoning resilient during transient states (e.g. non-mappable documents):
100
- // if namespace is missing, fall back to a stable graph derived from the model URI.
101
- const fallback = this.graphsFromModelUri(modelUri);
102
- this.modelGraphs.set(modelUri, fallback);
103
- return fallback;
104
- }
105
- graphsFromNamespace(namespace) {
106
- const ontologyIri = this.ontologyIriFromNamespace(namespace);
107
- return {
108
- own: namedNode(ontologyIri),
109
- entailments: namedNode(`${ontologyIri}__entailments`),
110
- };
91
+ const graphs = this.graphsFromModelUri(modelUri);
92
+ this.modelGraphs.set(modelUri, graphs);
93
+ return graphs;
111
94
  }
112
95
  graphsFromModelUri(modelUri) {
113
96
  return {
@@ -115,13 +98,6 @@ export class ReasoningStore {
115
98
  entailments: namedNode(`${modelUri}__entailments`)
116
99
  };
117
100
  }
118
- namespaceFromQuads(quads) {
119
- const namespaceQuad = quads.find((q) => q.predicate.value === OML_NAMESPACE && q.object.termType === 'NamedNode');
120
- return namespaceQuad?.object.value;
121
- }
122
- ontologyIriFromNamespace(namespace) {
123
- return namespace.replace(/[\/#]+$/, '');
124
- }
125
101
  requireGraphs(modelUri) {
126
102
  const graphs = this.modelGraphs.get(modelUri);
127
103
  if (!graphs) {
@@ -1 +1 @@
1
- {"version":3,"file":"owl-store.js","sourceRoot":"","sources":["../../src/owl/owl-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"owl-store.js","sourceRoot":"","sources":["../../src/owl/owl-store.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@oml/owl",
3
3
  "description": "The semantic web specific package",
4
- "version": "0.12.0",
4
+ "version": "0.14.0",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": ">=20.10.0",
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@comunica/query-sparql": "^5.1.3",
37
- "@oml/language": "0.12.0",
37
+ "@oml/language": "0.14.0",
38
38
  "langium": "^4.2.1",
39
39
  "n3": "^2.0.1",
40
40
  "shacl-engine": "^1.1.0",
@@ -880,51 +880,39 @@ export class ABoxChainer {
880
880
  }
881
881
  }
882
882
 
883
- private collectValidationWarnings(index: WorkingIndex, tbox: TBoxIndex): string[] {
883
+ private collectValidationWarnings(_index: WorkingIndex, _tbox: TBoxIndex): string[] {
884
884
  const warnings: string[] = [];
885
885
 
886
- for (const fact of index.facts.values()) {
887
- if (fact.predicate.value === RDF_TYPE.value) {
888
- continue;
889
- }
890
- const ranges = tbox.datatypeRange.get(fact.predicate.value) ?? [];
891
- if (ranges.length === 0) {
892
- continue;
893
- }
894
- if (fact.object.termType !== 'Literal') {
895
- warnings.push(`Datatype range violation on ${fact.predicate.value}: expected literal object.`);
896
- continue;
897
- }
898
- if (!ranges.includes(fact.object.datatype.value)) {
899
- warnings.push(
900
- `Datatype range violation on ${fact.predicate.value}: expected ${ranges.join(', ')}, got ${fact.object.datatype.value}.`,
901
- );
886
+ for (const propertyIri of _tbox.functionalProperties) {
887
+ const bySubject = new Map<string, Set<string>>();
888
+ const facts = _index.edgeFactsByProperty.get(propertyIri) ?? [];
889
+ for (const fact of facts) {
890
+ const objects = bySubject.get(fact.subject.id) ?? new Set<string>();
891
+ objects.add(fact.object.id);
892
+ bySubject.set(fact.subject.id, objects);
893
+ }
894
+ for (const [subjectId, objects] of bySubject.entries()) {
895
+ if (objects.size > 1) {
896
+ warnings.push(`Functional property violation: ${propertyIri} has multiple values for subject ${subjectId}`);
897
+ }
902
898
  }
903
899
  }
904
900
 
905
- for (const [subjectProperty, facts] of index.edgeFactsBySubjectProperty.entries()) {
906
- const [subjectId, property] = subjectProperty.split('|', 2);
907
- if (subjectId.length === 0 || !tbox.functionalProperties.has(property)) {
908
- continue;
909
- }
910
- const objectIds = [...new Set(facts.map((fact) => fact.object.id))];
911
- if (objectIds.length > 1) {
912
- warnings.push(`Functional property violation on ${property}: ${facts[0]?.subject.value ?? subjectId} has ${objectIds.length} distinct values.`);
901
+ for (const propertyIri of _tbox.inverseFunctionalProperties) {
902
+ const byObject = new Map<string, Set<string>>();
903
+ const facts = _index.edgeFactsByProperty.get(propertyIri) ?? [];
904
+ for (const fact of facts) {
905
+ const subjects = byObject.get(fact.object.id) ?? new Set<string>();
906
+ subjects.add(fact.subject.id);
907
+ byObject.set(fact.object.id, subjects);
913
908
  }
914
- }
915
-
916
- for (const [propertyObject, subjectIds] of index.subjectsByPropertyObject.entries()) {
917
- const split = propertyObject.indexOf('|');
918
- const property = split >= 0 ? propertyObject.slice(0, split) : propertyObject;
919
- const objectId = split >= 0 ? propertyObject.slice(split + 1) : '';
920
- if (!tbox.inverseFunctionalProperties.has(property) || subjectIds.size <= 1) {
921
- continue;
909
+ for (const [objectId, subjects] of byObject.entries()) {
910
+ if (subjects.size > 1) {
911
+ warnings.push(`Inverse-functional property violation: ${propertyIri} has multiple subjects for object ${objectId}`);
912
+ }
922
913
  }
923
- const object = index.termByKey.get(objectId);
924
- const objectLabel = object?.value ?? objectId;
925
- warnings.push(`Inverse-functional property violation on ${property}: ${objectLabel} has ${subjectIds.size} distinct sources.`);
926
914
  }
927
915
 
928
- return [...new Set(warnings)];
916
+ return warnings;
929
917
  }
930
918
  }
@@ -133,7 +133,7 @@ export interface OwlShaclValidationResult {
133
133
  }
134
134
 
135
135
  export interface OwlShaclService {
136
- validateShacl(modelUri: string | undefined, shaclSource: string): Promise<OwlShaclValidationResult>;
136
+ validateShacl(modelUri: string, shaclSource: string): Promise<OwlShaclValidationResult>;
137
137
  }
138
138
 
139
139
  export interface OwlReasoningDependencies {
@@ -53,6 +53,9 @@ export class ReasoningService {
53
53
  this.aboxChainer = resolved.aboxChainer;
54
54
  this.aboxEntailmentCache = resolved.aboxEntailmentCache;
55
55
  this.sparqlService = resolved.createSparqlService(this);
56
+ if (this.sparqlService instanceof SparqlService) {
57
+ this.sparqlService.setOntologyIriResolver((modelUri) => this.resolveOntologyIriForModelUri(modelUri));
58
+ }
56
59
  this.ontologyModelIndex = getOntologyModelIndex(services.shared);
57
60
 
58
61
  services.shared.workspace.DocumentBuilder.onDocumentPhase(DocumentState.Validated, (document) => {
@@ -677,15 +680,13 @@ function isWorkspaceModelUri(modelUri: string): boolean {
677
680
  }
678
681
 
679
682
  export function resolveCanonicalWorkspaceModelUri(modelUri: string): string {
680
- try {
681
- const parsed = URI.parse(modelUri);
682
- if (!parsed.path.toLowerCase().endsWith('.oml')) {
683
- return modelUri;
684
- }
685
- return parsed.with({ query: '', fragment: '' }).toString();
686
- } catch {
687
- return modelUri;
683
+ const windowsFileUri = /^file:\/\/\/([A-Za-z]):\/(.*)$/u.exec(modelUri);
684
+ if (windowsFileUri) {
685
+ const drive = windowsFileUri[1].toLowerCase();
686
+ const rest = windowsFileUri[2];
687
+ return `file:///${drive}%3A/${rest}`;
688
688
  }
689
+ return modelUri;
689
690
  }
690
691
 
691
692
  const BUILT_IN_ONTOLOGIES = new Set([
@@ -14,8 +14,8 @@ import type {
14
14
  export const ShaclValidateRequest = 'oml/validate';
15
15
 
16
16
  export interface ShaclValidateParams {
17
- modelUri?: string;
18
- shaclSource: string;
17
+ modelUri: string;
18
+ shacl: string;
19
19
  }
20
20
 
21
21
  export type ShaclValidationIssue = OwlShaclValidationIssue;
@@ -29,6 +29,8 @@ export type DerivedShaclSelectQuery = {
29
29
 
30
30
  type ParsedShaclColumn = {
31
31
  path: string;
32
+ pathVariableHint?: string;
33
+ readOnly: boolean;
32
34
  name?: string;
33
35
  };
34
36
 
@@ -36,7 +38,6 @@ type ParsedShaclNodeShape = {
36
38
  targetClasses: Term[];
37
39
  targetNodes: Term[];
38
40
  columns: ParsedShaclColumn[];
39
- includeEntailments: boolean;
40
41
  };
41
42
 
42
43
  type ConnectionLike = {
@@ -59,7 +60,15 @@ const SH_PATH = 'http://www.w3.org/ns/shacl#path';
59
60
  const SH_PROPERTY = 'http://www.w3.org/ns/shacl#property';
60
61
  const SH_TARGET_CLASS = 'http://www.w3.org/ns/shacl#targetClass';
61
62
  const SH_TARGET_NODE = 'http://www.w3.org/ns/shacl#targetNode';
62
- const OML_ENTAILED = 'http://opencaesar.io/oml#entailed';
63
+ const SH_ALTERNATIVE_PATH = 'http://www.w3.org/ns/shacl#alternativePath';
64
+ const SH_INVERSE_PATH = 'http://www.w3.org/ns/shacl#inversePath';
65
+ const SH_ZERO_OR_MORE_PATH = 'http://www.w3.org/ns/shacl#zeroOrMorePath';
66
+ const SH_ONE_OR_MORE_PATH = 'http://www.w3.org/ns/shacl#oneOrMorePath';
67
+ const SH_ZERO_OR_ONE_PATH = 'http://www.w3.org/ns/shacl#zeroOrOnePath';
68
+ const DASH_READ_ONLY = 'http://datashapes.org/dash#readOnly';
69
+ const RDF_FIRST = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first';
70
+ const RDF_REST = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest';
71
+ const RDF_NIL = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil';
63
72
  export class ShaclService implements OwlShaclService {
64
73
  constructor(
65
74
  private readonly sparqlService: OwlSparqlService,
@@ -67,15 +76,7 @@ export class ShaclService implements OwlShaclService {
67
76
  private readonly resolveContextIri: (modelUri: string) => string,
68
77
  ) {}
69
78
 
70
- async validateShacl(modelUri: string | undefined, shaclSource: string): Promise<ShaclValidationResult> {
71
- if (!modelUri) {
72
- return {
73
- conforms: false,
74
- issues: [],
75
- error: 'No contextUri model is available for this markdown document.',
76
- };
77
- }
78
-
79
+ async validateShacl(modelUri: string, shaclSource: string): Promise<ShaclValidationResult> {
79
80
  const trimmedSource = stripShaclFrontMatter(shaclSource).trim();
80
81
  if (!trimmedSource) {
81
82
  return {
@@ -105,8 +106,8 @@ export class ShaclService implements OwlShaclService {
105
106
  }
106
107
 
107
108
  await this.prepareContext(modelUri);
108
- const contextIri = this.resolveContextIri(modelUri);
109
- const constructResult = await this.sparqlService.construct(modelUri, buildValidationConstructQuery(contextIri));
109
+ const contextOntologyIri = this.resolveContextIri(modelUri);
110
+ const constructResult = await this.sparqlService.construct(modelUri, buildValidationConstructQuery(contextOntologyIri));
110
111
  if (!constructResult.success) {
111
112
  return {
112
113
  conforms: false,
@@ -172,8 +173,15 @@ export function registerShaclValidationRequests(connection: ConnectionLike, oml:
172
173
  error: 'Reasoning service validation is unavailable.',
173
174
  };
174
175
  }
175
- const modelUri = typeof params?.modelUri === 'string' ? params.modelUri : undefined;
176
- const shaclSource = typeof params?.shaclSource === 'string' ? params.shaclSource : '';
176
+ const modelUri = typeof params?.modelUri === 'string' ? params.modelUri.trim() : '';
177
+ const shaclSource = typeof params?.shacl === 'string' ? params.shacl : '';
178
+ if (!modelUri) {
179
+ return {
180
+ conforms: false,
181
+ issues: [],
182
+ error: 'Missing modelUri.',
183
+ };
184
+ }
177
185
  const shaclService = createDefaultShaclService(
178
186
  reasoningService.getSparqlService(),
179
187
  (uri) => reasoningService.ensureQueryContext(uri),
@@ -199,13 +207,14 @@ export function deriveSelectQueryFromShacl(shaclSource: string, graphIri: string
199
207
 
200
208
  const usedNames = new Set<string>(['focus']);
201
209
  const variables = nodeShape.columns.map((column) => {
202
- const base = normalizeSparqlVariableName(column.name || localName(column.path) || 'value');
210
+ const base = normalizeSparqlVariableName(column.name || column.pathVariableHint || 'value');
203
211
  const unique = uniqueVariableName(base, usedNames);
204
212
  usedNames.add(unique);
205
213
  const sourceVariable = uniqueVariableName(`__${unique}`, usedNames);
206
214
  usedNames.add(sourceVariable);
207
215
  return {
208
216
  path: column.path,
217
+ readOnly: column.readOnly,
209
218
  variable: unique,
210
219
  sourceVariable,
211
220
  name: column.name,
@@ -221,9 +230,7 @@ export function deriveSelectQueryFromShacl(shaclSource: string, graphIri: string
221
230
  const whereLines: string[] = [];
222
231
  const graphLines: string[] = [];
223
232
  const graphPattern = (pattern: string): string =>
224
- nodeShape.includeEntailments
225
- ? `{ GRAPH <${escapeIriForSparql(graphIri)}> { ${pattern} } } UNION { GRAPH <${escapeIriForSparql(`${graphIri}__entailments`)}> { ${pattern} } }`
226
- : `GRAPH <${escapeIriForSparql(graphIri)}> { ${pattern} }`;
233
+ `GRAPH <${escapeIriForSparql(graphIri)}> { ${pattern} }`;
227
234
 
228
235
  if (nodeShape.targetClasses.length > 0) {
229
236
  const values = nodeShape.targetClasses.map((term: Term) => `<${term.value}>`).join(' ');
@@ -237,7 +244,12 @@ export function deriveSelectQueryFromShacl(shaclSource: string, graphIri: string
237
244
  }
238
245
 
239
246
  for (const entry of variables) {
240
- graphLines.push(` OPTIONAL { ${graphPattern(`?focus <${entry.path}> ?${entry.sourceVariable} .`)} }`);
247
+ const pattern = `?focus ${entry.path} ?${entry.sourceVariable} .`;
248
+ if (entry.readOnly) {
249
+ graphLines.push(` OPTIONAL { ${pattern} }`);
250
+ } else {
251
+ graphLines.push(` OPTIONAL { ${graphPattern(pattern)} }`);
252
+ }
241
253
  }
242
254
  whereLines.push(...graphLines);
243
255
 
@@ -260,7 +272,7 @@ export function deriveSelectQueryFromShacl(shaclSource: string, graphIri: string
260
272
  };
261
273
  }
262
274
 
263
- function buildValidationConstructQuery(contextIri: string): string {
275
+ function buildValidationConstructQuery(contextOntologyIri: string): string {
264
276
  return [
265
277
  'CONSTRUCT {',
266
278
  ' ?s ?p ?o .',
@@ -268,11 +280,11 @@ function buildValidationConstructQuery(contextIri: string): string {
268
280
  ' ?o a ?oType .',
269
281
  '}',
270
282
  'WHERE {',
271
- ` GRAPH <${contextIri}> { ?s ?p ?o }`,
272
- ` OPTIONAL { GRAPH ?g1 { ?s a ?sType } FILTER(?g1 != <${contextIri}>) }`,
283
+ ` GRAPH <${contextOntologyIri}> { ?s ?p ?o }`,
284
+ ` OPTIONAL { GRAPH ?g1 { ?s a ?sType } FILTER(?g1 != <${contextOntologyIri}>) }`,
273
285
  ' OPTIONAL {',
274
286
  ' FILTER(isIRI(?o) || isBlank(?o))',
275
- ` GRAPH ?g2 { ?o a ?oType } FILTER(?g2 != <${contextIri}>)`,
287
+ ` GRAPH ?g2 { ?o a ?oType } FILTER(?g2 != <${contextOntologyIri}>)`,
276
288
  ' }',
277
289
  '}',
278
290
  ].join('\n');
@@ -299,24 +311,27 @@ function parsePrimaryNodeShape(shapeQuads: readonly Quad[]): ParsedShaclNodeShap
299
311
  .filter((term) => !isDeactivatedShape(shapesStore, term))
300
312
  .map((term): ParsedShaclColumn | undefined => {
301
313
  const path = getFirstObject(shapesStore, term, SH_PATH);
302
- if (!path || path.termType !== 'NamedNode') {
314
+ if (!path) {
315
+ return undefined;
316
+ }
317
+ const parsedPath = parseShaclPathToSparql(shapesStore, path);
318
+ if (!parsedPath) {
303
319
  return undefined;
304
320
  }
305
321
  const nameTerm = getFirstObject(shapesStore, term, SH_NAME);
306
322
  return {
307
- path: path.value,
323
+ path: parsedPath.sparql,
324
+ pathVariableHint: parsedPath.variableHint,
325
+ readOnly: isReadOnlyShape(shapesStore, term),
308
326
  name: nameTerm && nameTerm.termType === 'Literal' ? nameTerm.value : undefined,
309
327
  };
310
328
  })
311
329
  .filter((column): column is ParsedShaclColumn => Boolean(column));
312
- const includeEntailments = getObjects(shapesStore, shapeTerm, SH_PROPERTY)
313
- .some((term) => isEntailedShape(shapesStore, term));
314
330
 
315
331
  return {
316
332
  targetClasses,
317
333
  targetNodes,
318
334
  columns,
319
- includeEntailments,
320
335
  };
321
336
  }
322
337
 
@@ -353,8 +368,8 @@ function isTruthyLiteral(term: Term | undefined): boolean {
353
368
  return term?.termType === 'Literal' && (term.value === 'true' || term.value === '1');
354
369
  }
355
370
 
356
- function isEntailedShape(store: N3Store, subject: Term): boolean {
357
- return getObjects(store, subject, OML_ENTAILED).some((term) => isTruthyLiteral(term));
371
+ function isReadOnlyShape(store: N3Store, subject: Term): boolean {
372
+ return getObjects(store, subject, DASH_READ_ONLY).some((term) => isTruthyLiteral(term));
358
373
  }
359
374
 
360
375
  function getObjects(store: N3Store, subject: Term, predicateIri: string): Term[] {
@@ -511,3 +526,127 @@ function localName(iri: string): string {
511
526
  function escapeIriForSparql(iri: string): string {
512
527
  return iri.replace(/>/g, '\\>');
513
528
  }
529
+
530
+ type ParsedSparqlPath = {
531
+ sparql: string;
532
+ variableHint: string;
533
+ };
534
+
535
+ function parseShaclPathToSparql(store: N3Store, term: Term): ParsedSparqlPath | undefined {
536
+ if (term.termType === 'NamedNode') {
537
+ return {
538
+ sparql: `<${term.value}>`,
539
+ variableHint: localName(term.value),
540
+ };
541
+ }
542
+
543
+ const sequence = parseRdfList(store, term);
544
+ if (sequence && sequence.length > 0) {
545
+ const parts = sequence
546
+ .map((entry) => parseShaclPathToSparql(store, entry))
547
+ .filter((entry): entry is ParsedSparqlPath => Boolean(entry));
548
+ if (parts.length !== sequence.length) {
549
+ return undefined;
550
+ }
551
+ return {
552
+ sparql: parts.map((entry) => wrapPath(entry.sparql)).join('/'),
553
+ variableHint: parts[parts.length - 1]?.variableHint ?? 'value',
554
+ };
555
+ }
556
+
557
+ if (term.termType !== 'BlankNode') {
558
+ return undefined;
559
+ }
560
+
561
+ const alternativePath = getFirstObject(store, term, SH_ALTERNATIVE_PATH);
562
+ if (alternativePath) {
563
+ const options = parseRdfList(store, alternativePath);
564
+ if (!options || options.length === 0) {
565
+ return undefined;
566
+ }
567
+ const parts = options
568
+ .map((entry) => parseShaclPathToSparql(store, entry))
569
+ .filter((entry): entry is ParsedSparqlPath => Boolean(entry));
570
+ if (parts.length !== options.length) {
571
+ return undefined;
572
+ }
573
+ const hintParts = [...new Set(parts.map((entry) => entry.variableHint).filter(Boolean))];
574
+ return {
575
+ sparql: parts.map((entry) => wrapPath(entry.sparql)).join('|'),
576
+ variableHint: hintParts.length > 0 ? hintParts.join('_or_') : 'value',
577
+ };
578
+ }
579
+
580
+ const inversePath = getFirstObject(store, term, SH_INVERSE_PATH);
581
+ if (inversePath) {
582
+ const parsed = parseShaclPathToSparql(store, inversePath);
583
+ if (!parsed) {
584
+ return undefined;
585
+ }
586
+ return {
587
+ sparql: `^${wrapPath(parsed.sparql)}`,
588
+ variableHint: parsed.variableHint,
589
+ };
590
+ }
591
+
592
+ const quantified = [
593
+ { predicate: SH_ZERO_OR_MORE_PATH, quantifier: '*' },
594
+ { predicate: SH_ONE_OR_MORE_PATH, quantifier: '+' },
595
+ { predicate: SH_ZERO_OR_ONE_PATH, quantifier: '?' },
596
+ ] as const;
597
+ for (const entry of quantified) {
598
+ const target = getFirstObject(store, term, entry.predicate);
599
+ if (!target) {
600
+ continue;
601
+ }
602
+ const parsed = parseShaclPathToSparql(store, target);
603
+ if (!parsed) {
604
+ return undefined;
605
+ }
606
+ return {
607
+ sparql: `${wrapPath(parsed.sparql)}${entry.quantifier}`,
608
+ variableHint: parsed.variableHint,
609
+ };
610
+ }
611
+
612
+ return undefined;
613
+ }
614
+
615
+ function parseRdfList(store: N3Store, term: Term): Term[] | undefined {
616
+ if (term.termType !== 'BlankNode' && term.termType !== 'NamedNode') {
617
+ return undefined;
618
+ }
619
+ if (term.termType === 'NamedNode' && term.value === RDF_NIL) {
620
+ return [];
621
+ }
622
+ const visited = new Set<string>();
623
+ const items: Term[] = [];
624
+ let current: Term = term;
625
+ while (true) {
626
+ if (current.termType === 'NamedNode' && current.value === RDF_NIL) {
627
+ return items;
628
+ }
629
+ const key = termKey(current);
630
+ if (!key || visited.has(key)) {
631
+ return undefined;
632
+ }
633
+ visited.add(key);
634
+ const head = getFirstObject(store, current, RDF_FIRST);
635
+ const tail = getFirstObject(store, current, RDF_REST);
636
+ if (!head || !tail) {
637
+ return undefined;
638
+ }
639
+ items.push(head);
640
+ current = tail;
641
+ }
642
+ }
643
+
644
+ function wrapPath(path: string): string {
645
+ if (!path || /^<[^>]+>$/.test(path)) {
646
+ return path;
647
+ }
648
+ if (/^\^[^|/*+?]+$/.test(path)) {
649
+ return path;
650
+ }
651
+ return `(${path})`;
652
+ }