@oml/owl 0.7.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.
Files changed (46) hide show
  1. package/README.md +46 -0
  2. package/out/index.d.ts +10 -0
  3. package/out/index.js +12 -0
  4. package/out/index.js.map +1 -0
  5. package/out/owl/owl-abox.d.ts +79 -0
  6. package/out/owl/owl-abox.js +765 -0
  7. package/out/owl/owl-abox.js.map +1 -0
  8. package/out/owl/owl-imports.d.ts +9 -0
  9. package/out/owl/owl-imports.js +102 -0
  10. package/out/owl/owl-imports.js.map +1 -0
  11. package/out/owl/owl-interfaces.d.ts +121 -0
  12. package/out/owl/owl-interfaces.js +3 -0
  13. package/out/owl/owl-interfaces.js.map +1 -0
  14. package/out/owl/owl-mapper.d.ts +80 -0
  15. package/out/owl/owl-mapper.js +1217 -0
  16. package/out/owl/owl-mapper.js.map +1 -0
  17. package/out/owl/owl-service.d.ts +65 -0
  18. package/out/owl/owl-service.js +552 -0
  19. package/out/owl/owl-service.js.map +1 -0
  20. package/out/owl/owl-shacl.d.ts +28 -0
  21. package/out/owl/owl-shacl.js +337 -0
  22. package/out/owl/owl-shacl.js.map +1 -0
  23. package/out/owl/owl-sparql.d.ts +71 -0
  24. package/out/owl/owl-sparql.js +260 -0
  25. package/out/owl/owl-sparql.js.map +1 -0
  26. package/out/owl/owl-store.d.ts +32 -0
  27. package/out/owl/owl-store.js +142 -0
  28. package/out/owl/owl-store.js.map +1 -0
  29. package/out/owl/owl-tbox.d.ts +98 -0
  30. package/out/owl/owl-tbox.js +575 -0
  31. package/out/owl/owl-tbox.js.map +1 -0
  32. package/out/owl-module.d.ts +15 -0
  33. package/out/owl-module.js +22 -0
  34. package/out/owl-module.js.map +1 -0
  35. package/package.json +52 -0
  36. package/src/index.ts +12 -0
  37. package/src/owl/owl-abox.ts +930 -0
  38. package/src/owl/owl-imports.ts +108 -0
  39. package/src/owl/owl-interfaces.ts +145 -0
  40. package/src/owl/owl-mapper.ts +1510 -0
  41. package/src/owl/owl-service.ts +642 -0
  42. package/src/owl/owl-shacl.ts +400 -0
  43. package/src/owl/owl-sparql.ts +317 -0
  44. package/src/owl/owl-store.ts +173 -0
  45. package/src/owl/owl-tbox.ts +727 -0
  46. package/src/owl-module.ts +52 -0
@@ -0,0 +1,642 @@
1
+ // Copyright (c) 2026 Modelware. All rights reserved.
2
+
3
+ import { DocumentState, URI, type LangiumDocument } from 'langium';
4
+ import type { NamedNode, Quad } from 'n3';
5
+ import { OntologyModelIndex, getOntologyModelIndex, isDescription, isDescriptionBundle, isOntology, isVocabulary, isVocabularyBundle, type Import, type OmlServices, type Ontology } from '@oml/language';
6
+ import { Oml2OwlMapper } from './owl-mapper.js';
7
+ import { ReasoningStore } from './owl-store.js';
8
+ import { ABoxChainer, ABoxEntailmentCache, type ChainingResult } from './owl-abox.js';
9
+ import { ImportGraph } from './owl-imports.js';
10
+ import { SparqlService } from './owl-sparql.js';
11
+ import { TBoxChainer, TBoxIndexBuilder, TBoxIndexCache } from './owl-tbox.js';
12
+ import type {
13
+ OwlABoxChainer,
14
+ OwlABoxEntailmentState,
15
+ OwlImportGraph,
16
+ OwlMapper,
17
+ OwlReasoningDependencies,
18
+ OwlSparqlService,
19
+ OwlStore,
20
+ OwlTBoxChainer,
21
+ OwlTBoxIndexBuilder,
22
+ OwlTBoxIndexCache
23
+ } from './owl-interfaces.js';
24
+
25
+ export class ReasoningService {
26
+ private readonly services: OmlServices;
27
+ private readonly mapper: OwlMapper;
28
+ private readonly reasoningStore: OwlStore;
29
+ private readonly tboxChainer: OwlTBoxChainer;
30
+ private readonly tboxIndexBuilder: OwlTBoxIndexBuilder;
31
+ private readonly tboxIndexCache: OwlTBoxIndexCache;
32
+ private readonly aboxChainer: OwlABoxChainer;
33
+ private readonly aboxEntailmentCache: OwlABoxEntailmentState;
34
+ private readonly importGraph: OwlImportGraph;
35
+ private readonly sparqlService: OwlSparqlService;
36
+ private readonly ontologyModelIndex: OntologyModelIndex;
37
+ private readonly preparedModelAliases = new Map<string, string>();
38
+ private readonly activeDocuments = new Set<string>();
39
+ private readonly modelLoadInFlight = new Map<string, Promise<void>>();
40
+ private readonly semanticChangeListeners = new Set<(modelUris: string[]) => void>();
41
+ private workspaceBuildPromise: Promise<void> | undefined;
42
+ private workspaceBuilt = false;
43
+
44
+ constructor(services: OmlServices, dependencies: Partial<OwlReasoningDependencies> = {}) {
45
+ this.services = services;
46
+ const resolved = createDefaultDependencies(dependencies);
47
+ this.mapper = resolved.mapper;
48
+ this.reasoningStore = resolved.reasoningStore;
49
+ this.importGraph = resolved.importGraph;
50
+ this.tboxIndexCache = resolved.tboxIndexCache;
51
+ this.tboxChainer = resolved.tboxChainer;
52
+ this.tboxIndexBuilder = resolved.tboxIndexBuilder;
53
+ this.aboxChainer = resolved.aboxChainer;
54
+ this.aboxEntailmentCache = resolved.aboxEntailmentCache;
55
+ this.sparqlService = resolved.createSparqlService(this);
56
+ this.ontologyModelIndex = getOntologyModelIndex(services.shared);
57
+
58
+ services.shared.workspace.DocumentBuilder.onDocumentPhase(DocumentState.Validated, (document) => {
59
+ this.onDocumentValidated(document);
60
+ });
61
+ }
62
+
63
+ onDocumentValidated(document: LangiumDocument): void {
64
+ const ontology = document.parseResult.value;
65
+ if (!isOntology(ontology)) {
66
+ return;
67
+ }
68
+
69
+ const modelUri = document.uri.toString();
70
+ const importedModelUris = this.extractImportedModelUris(ontology as Ontology);
71
+ this.importGraph.update(modelUri, importedModelUris);
72
+
73
+ const quads = this.mapper.toQuads(ontology as Ontology);
74
+ const diff = this.reasoningStore.diffModel(modelUri, quads);
75
+ if (diff.isEmpty) {
76
+ return;
77
+ }
78
+ this.tboxIndexCache.invalidateMerged(modelUri);
79
+ this.reasoningStore.retractModel(modelUri);
80
+ this.reasoningStore.loadModel(modelUri, quads);
81
+ const impactedByLoad = [modelUri, ...this.importGraph.dependentsOf(modelUri)];
82
+ for (const uri of impactedByLoad) {
83
+ this.sparqlService.invalidateFilteredStore(uri);
84
+ }
85
+ this.notifySemanticChanged(impactedByLoad);
86
+ if (isVocabulary(ontology)) {
87
+ this.tboxChainer.chain(modelUri);
88
+ const tboxIndex = this.tboxIndexBuilder.buildOwn(modelUri, ontology);
89
+ this.tboxIndexCache.setOwn(modelUri, tboxIndex);
90
+ const dependents = this.importGraph.dependentsOf(modelUri);
91
+ this.tboxIndexCache.invalidateMergedAll(dependents);
92
+ this.aboxEntailmentCache.markDirtyAll(dependents);
93
+ for (const dependent of dependents) {
94
+ if (!this.isActiveDocument(dependent)) {
95
+ continue;
96
+ }
97
+ this.tboxIndexCache.setMerged(dependent, this.tboxIndexBuilder.buildMerged(dependent));
98
+ }
99
+ } else if (isVocabularyBundle(ontology)) {
100
+ const dependents = this.importGraph.dependentsOf(modelUri);
101
+ const affected = [modelUri, ...dependents];
102
+ this.tboxIndexCache.invalidateMergedAll(affected);
103
+ this.aboxEntailmentCache.markDirtyAll(affected);
104
+ for (const uri of affected) {
105
+ if (!this.isActiveDocument(uri)) {
106
+ continue;
107
+ }
108
+ this.tboxIndexCache.setMerged(uri, this.tboxIndexBuilder.buildMerged(uri));
109
+ }
110
+ } else if (isDescription(ontology)) {
111
+ this.aboxEntailmentCache.markDirty(modelUri);
112
+ } else if (isDescriptionBundle(ontology)) {
113
+ const dependents = this.importGraph.dependentsOf(modelUri);
114
+ const affected = [modelUri, ...dependents];
115
+ this.tboxIndexCache.invalidateMergedAll(affected);
116
+ this.aboxEntailmentCache.markDirtyAll(affected);
117
+ for (const uri of affected) {
118
+ if (!this.isActiveDocument(uri)) {
119
+ continue;
120
+ }
121
+ this.tboxIndexCache.setMerged(uri, this.tboxIndexBuilder.buildMerged(uri));
122
+ }
123
+ }
124
+ }
125
+
126
+ runInference(modelUri: string): ChainingResult {
127
+ const chainOrder = this.resolveLoadedDependencyOrder(modelUri);
128
+ let complete = true;
129
+ let iterations = 0;
130
+ let newQuadsCount = 0;
131
+ const chainedModels: string[] = [];
132
+
133
+ for (const chainedModelUri of chainOrder) {
134
+ const shouldChain =
135
+ this.aboxEntailmentCache.isDirty(chainedModelUri)
136
+ || (chainedModelUri === modelUri && this.aboxEntailmentCache.isAbsent(chainedModelUri));
137
+ if (!shouldChain) {
138
+ continue;
139
+ }
140
+
141
+ this.ensureOwnTboxIndexesForDependencies(chainedModelUri);
142
+ let merged = this.tboxIndexCache.getMerged(chainedModelUri);
143
+ if (!merged || this.tboxIndexCache.isMergedDirty(chainedModelUri)) {
144
+ merged = this.tboxIndexBuilder.buildMerged(chainedModelUri);
145
+ this.tboxIndexCache.setMerged(chainedModelUri, merged);
146
+ }
147
+
148
+ const readScope = this.resolveLoadedDependencyOrder(chainedModelUri);
149
+ const delta = this.aboxEntailmentCache.consumeDelta(chainedModelUri);
150
+ const result = this.aboxChainer.chain(chainedModelUri, merged, readScope, delta.plus, delta.minus);
151
+ this.aboxEntailmentCache.setValidationWarnings(chainedModelUri, result.validationWarnings);
152
+ chainedModels.push(chainedModelUri);
153
+ complete = complete && result.complete;
154
+ iterations += result.iterations;
155
+ newQuadsCount += result.newQuadsCount;
156
+ if (result.complete) {
157
+ this.aboxEntailmentCache.markClean(chainedModelUri);
158
+ } else {
159
+ this.aboxEntailmentCache.markDirty(chainedModelUri);
160
+ }
161
+ }
162
+
163
+ if (!chainedModels.includes(modelUri)) {
164
+ return { complete: true, iterations: 0, newQuadsCount: 0, validationWarnings: [] };
165
+ }
166
+ return { complete, iterations, newQuadsCount, validationWarnings: [] };
167
+ }
168
+
169
+ removeModel(modelUri: string): void {
170
+ const dependents = this.importGraph.dependentsOf(modelUri);
171
+ this.importGraph.remove(modelUri);
172
+ this.reasoningStore.retractModel(modelUri);
173
+ this.ontologyModelIndex.removeModel(modelUri);
174
+ this.tboxIndexCache.invalidateOwn(modelUri);
175
+ this.tboxIndexCache.invalidateMerged(modelUri);
176
+ this.aboxEntailmentCache.invalidate(modelUri);
177
+ this.tboxIndexCache.invalidateMergedAll(dependents);
178
+ this.aboxEntailmentCache.markDirtyAll(dependents);
179
+ this.sparqlService.removeFilteredStore(modelUri);
180
+ for (const dependent of dependents) {
181
+ this.sparqlService.invalidateFilteredStore(dependent);
182
+ }
183
+ this.notifySemanticChanged([modelUri, ...dependents]);
184
+ this.activeDocuments.delete(modelUri);
185
+ }
186
+
187
+ onSemanticChanged(listener: (modelUris: string[]) => void): () => void {
188
+ this.semanticChangeListeners.add(listener);
189
+ return () => {
190
+ this.semanticChangeListeners.delete(listener);
191
+ };
192
+ }
193
+
194
+ getStore(): OwlStore {
195
+ return this.reasoningStore;
196
+ }
197
+
198
+ getImportGraph(): OwlImportGraph {
199
+ return this.importGraph;
200
+ }
201
+
202
+ getSparqlService(): OwlSparqlService {
203
+ return {
204
+ invalidateFilteredStore: (modelUri: string) => {
205
+ this.sparqlService.invalidateFilteredStore(this.resolveQueryModelUri(modelUri));
206
+ },
207
+ removeFilteredStore: (modelUri: string) => {
208
+ this.sparqlService.removeFilteredStore(this.resolveQueryModelUri(modelUri));
209
+ },
210
+ query: (modelUri: string, sparql: string) => {
211
+ return this.sparqlService.query(this.resolveQueryModelUri(modelUri), sparql);
212
+ },
213
+ construct: (modelUri: string, sparql: string) => {
214
+ return this.sparqlService.construct(this.resolveQueryModelUri(modelUri), sparql);
215
+ },
216
+ };
217
+ }
218
+
219
+ loadPreparedDataset(models: ReadonlyArray<PreparedReasonedModel>): void {
220
+ this.preparedModelAliases.clear();
221
+ for (const model of models) {
222
+ this.preparedModelAliases.set(model.modelUri, model.modelUri);
223
+ for (const alias of model.aliases ?? []) {
224
+ this.preparedModelAliases.set(alias, model.modelUri);
225
+ }
226
+ this.importGraph.update(model.modelUri, [...model.imports]);
227
+ this.reasoningStore.loadModel(model.modelUri, [...model.assertedQuads]);
228
+ this.reasoningStore.loadEntailments(model.modelUri, [...model.entailedQuads]);
229
+ this.aboxEntailmentCache.markClean(model.modelUri);
230
+ this.aboxEntailmentCache.setValidationWarnings(model.modelUri, [...(model.validationWarnings ?? [])]);
231
+ this.sparqlService.invalidateFilteredStore(model.modelUri);
232
+ }
233
+ this.workspaceBuilt = true;
234
+ this.workspaceBuildPromise = undefined;
235
+ }
236
+
237
+ async ensureQueryContext(modelUri: string): Promise<void> {
238
+ const resolvedModelUri = this.resolveQueryModelUri(modelUri);
239
+ await this.ensureWorkspaceBuilt();
240
+ await this.ensureModelLoaded(resolvedModelUri);
241
+ await this.ensureContextDatasetReady(resolvedModelUri);
242
+ }
243
+
244
+ getContextIri(modelUri: string): string {
245
+ return this.resolveOntologyIriForModelUri(this.resolveQueryModelUri(modelUri));
246
+ }
247
+
248
+ setActiveDocuments(modelUris: string[]): void {
249
+ this.activeDocuments.clear();
250
+ for (const modelUri of modelUris) {
251
+ this.activeDocuments.add(modelUri);
252
+ }
253
+ }
254
+
255
+ markDocumentActive(modelUri: string): void {
256
+ this.activeDocuments.add(modelUri);
257
+ }
258
+
259
+ markDocumentInactive(modelUri: string): void {
260
+ this.activeDocuments.delete(modelUri);
261
+ }
262
+
263
+ private extractImportedModelUris(ontology: Ontology): string[] {
264
+ const prefixes = this.buildPrefixMap(ontology);
265
+ const imports = ontology.ownedImports ?? [];
266
+ const importedModelUris = imports
267
+ .map((ownedImport) => this.resolveImportedModelUri(ownedImport, prefixes))
268
+ .filter((uri): uri is string => Boolean(uri));
269
+ return [...new Set(importedModelUris)];
270
+ }
271
+
272
+ private resolveImportedModelUri(ownedImport: Import, prefixes: Map<string, string>): string | undefined {
273
+ if (ownedImport.imported?.ref && isOntology(ownedImport.imported.ref)) {
274
+ const importedDocumentUri = (ownedImport.imported.ref as any)?.$document?.uri?.toString();
275
+ if (importedDocumentUri) {
276
+ return importedDocumentUri;
277
+ }
278
+ const importedNamespace = this.normalizeNamespace((ownedImport.imported.ref as any).namespace ?? '');
279
+ return this.resolveModelUriFromOntologyIdentifier(importedNamespace);
280
+ }
281
+ const refText = (ownedImport.imported as any)?.$refText ?? (ownedImport.imported as any)?.refText;
282
+ if (!refText) {
283
+ return undefined;
284
+ }
285
+ const ontologyIdentifier = this.resolveFromRefText(refText, prefixes);
286
+ if (!ontologyIdentifier || BUILT_IN_ONTOLOGIES.has(ontologyIdentifier)) {
287
+ return undefined;
288
+ }
289
+ return this.resolveModelUriFromOntologyIdentifier(ontologyIdentifier);
290
+ }
291
+
292
+ private buildPrefixMap(ontology: Ontology): Map<string, string> {
293
+ const map = new Map<string, string>();
294
+ const ontologyPrefix = (ontology as any).prefix;
295
+ const ontologyNamespace = this.normalizeNamespace((ontology as any).namespace ?? '');
296
+ if (ontologyPrefix && ontologyNamespace) {
297
+ map.set(ontologyPrefix, ontologyNamespace);
298
+ }
299
+ for (const ownedImport of ontology.ownedImports ?? []) {
300
+ const imported = ownedImport.imported?.ref;
301
+ const importedPrefix = ownedImport.prefix ?? (imported as any)?.prefix;
302
+ const importedNamespace = imported ? this.normalizeNamespace((imported as any).namespace ?? '') : undefined;
303
+ if (importedPrefix && importedNamespace) {
304
+ map.set(importedPrefix, importedNamespace);
305
+ }
306
+ }
307
+ return map;
308
+ }
309
+
310
+ private resolveFromRefText(refText: string, prefixes: Map<string, string>): string | undefined {
311
+ if (refText.startsWith('<') && refText.endsWith('>')) {
312
+ return refText.slice(1, -1);
313
+ }
314
+ const parts = refText.split(':');
315
+ if (parts.length === 2) {
316
+ const namespace = prefixes.get(parts[0]);
317
+ if (namespace) {
318
+ return `${namespace}${parts[1]}`;
319
+ }
320
+ }
321
+ return undefined;
322
+ }
323
+
324
+ private normalizeNamespace(namespace: string): string {
325
+ return namespace.replace(/^<|>$/g, '');
326
+ }
327
+
328
+ private resolveModelUriFromOntologyIdentifier(identifier: string): string | undefined {
329
+ return this.ontologyModelIndex.resolveModelUri(identifier);
330
+ }
331
+
332
+ private resolveQueryModelUri(modelUri: string): string {
333
+ return this.resolveWorkspaceModelUri(modelUri) ?? modelUri;
334
+ }
335
+
336
+ private isActiveDocument(modelUri: string): boolean {
337
+ return this.activeDocuments.has(modelUri);
338
+ }
339
+
340
+ private resolveOntologyIriForModelUri(modelUri: string): string {
341
+ const resolvedModelUri = this.resolveWorkspaceModelUri(modelUri) ?? modelUri;
342
+ return this.ontologyModelIndex.resolveOntologyIri(resolvedModelUri)
343
+ ?? this.reasoningStore.graphs(resolvedModelUri).own.value;
344
+ }
345
+ private async ensureModelLoaded(modelUri: string): Promise<void> {
346
+ try {
347
+ this.reasoningStore.graphs(modelUri);
348
+ return;
349
+ } catch {
350
+ // fall through to on-demand load
351
+ }
352
+
353
+ const inFlight = this.modelLoadInFlight.get(modelUri);
354
+ if (inFlight) {
355
+ await inFlight;
356
+ } else {
357
+ const loadPromise = (async () => {
358
+ const uri = URI.parse(modelUri);
359
+ let document = this.services.shared.workspace.LangiumDocuments.getDocument(uri);
360
+ if (!document) {
361
+ try {
362
+ document = await this.services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri);
363
+ } catch {
364
+ // Another concurrent caller may have created it first.
365
+ document = this.services.shared.workspace.LangiumDocuments.getDocument(uri);
366
+ if (!document) {
367
+ throw new Error(`Unable to access model document '${modelUri}'.`);
368
+ }
369
+ }
370
+ }
371
+ await this.services.shared.workspace.DocumentBuilder.build([document], { validation: true });
372
+ })().finally(() => {
373
+ this.modelLoadInFlight.delete(modelUri);
374
+ });
375
+
376
+ this.modelLoadInFlight.set(modelUri, loadPromise);
377
+ await loadPromise;
378
+ }
379
+
380
+ try {
381
+ this.reasoningStore.graphs(modelUri);
382
+ } catch {
383
+ throw new Error(`Unknown model '${modelUri}'. Load the model first.`);
384
+ }
385
+ }
386
+
387
+ private async ensureWorkspaceBuilt(): Promise<void> {
388
+ if (this.workspaceBuilt) {
389
+ return;
390
+ }
391
+ if (this.workspaceBuildPromise) {
392
+ await this.workspaceBuildPromise;
393
+ return;
394
+ }
395
+
396
+ this.workspaceBuildPromise = (async () => {
397
+ const workspace = this.services.shared.workspace.WorkspaceManager;
398
+ const documents = this.services.shared.workspace.LangiumDocuments;
399
+ const builder = this.services.shared.workspace.DocumentBuilder;
400
+ await workspace.ready;
401
+
402
+ const folderUris = workspace.workspaceFolders ?? [];
403
+ const omlUris = new Set<string>();
404
+ for (const folder of folderUris) {
405
+ const entries = await workspace.searchFolder(URI.parse(folder.uri));
406
+ for (const entry of entries) {
407
+ const uri = entry.toString();
408
+ if (uri.toLowerCase().endsWith('.oml')) {
409
+ omlUris.add(uri);
410
+ }
411
+ }
412
+ }
413
+
414
+ if (omlUris.size === 0) {
415
+ this.workspaceBuilt = true;
416
+ return;
417
+ }
418
+
419
+ const docs = await Promise.all(
420
+ [...omlUris].map((uri) => documents.getOrCreateDocument(URI.parse(uri)))
421
+ );
422
+ await builder.build(docs, { validation: true });
423
+ this.workspaceBuilt = true;
424
+ })().finally(() => {
425
+ this.workspaceBuildPromise = undefined;
426
+ });
427
+
428
+ await this.workspaceBuildPromise;
429
+ }
430
+
431
+ private async ensureContextDatasetReady(modelUri: string): Promise<void> {
432
+ const attemptedLoads = new Set<string>();
433
+
434
+ while (true) {
435
+ const allUris = [modelUri, ...this.importGraph.dependenciesOf(modelUri)];
436
+ const unresolvedIdentifiers: string[] = [];
437
+ const resolvedModelUris: string[] = [];
438
+ const seenResolved = new Set<string>();
439
+ for (const uri of allUris) {
440
+ const workspaceModelUri = this.resolveWorkspaceModelUri(uri);
441
+ if (!workspaceModelUri) {
442
+ unresolvedIdentifiers.push(uri);
443
+ continue;
444
+ }
445
+ if (!seenResolved.has(workspaceModelUri)) {
446
+ seenResolved.add(workspaceModelUri);
447
+ resolvedModelUris.push(workspaceModelUri);
448
+ }
449
+ }
450
+
451
+ const unloadedModelUris = resolvedModelUris.filter((uri) => !this.isModelLoaded(uri));
452
+ if (unresolvedIdentifiers.length === 0 && unloadedModelUris.length === 0) {
453
+ return;
454
+ }
455
+
456
+ const loadFailures: string[] = [];
457
+ let loadedAny = false;
458
+ for (const uri of unloadedModelUris) {
459
+ if (attemptedLoads.has(uri)) {
460
+ continue;
461
+ }
462
+ attemptedLoads.add(uri);
463
+ try {
464
+ await this.ensureModelLoaded(uri);
465
+ loadedAny = true;
466
+ } catch (error) {
467
+ const message = error instanceof Error ? error.message : String(error);
468
+ loadFailures.push(`${uri} (${message})`);
469
+ }
470
+ }
471
+ if (loadFailures.length > 0) {
472
+ throw new Error(
473
+ `Context dataset could not include the full import closure for '${modelUri}'. `
474
+ + `Failed to load models: ${loadFailures.join(', ')}`
475
+ );
476
+ }
477
+ if (loadedAny) {
478
+ continue;
479
+ }
480
+
481
+ const unresolved = [...new Set(unresolvedIdentifiers)];
482
+ const unloaded = [...new Set(unloadedModelUris)];
483
+ const details: string[] = [];
484
+ if (unresolved.length > 0) {
485
+ details.push(`unresolved imports: ${unresolved.join(', ')}`);
486
+ }
487
+ if (unloaded.length > 0) {
488
+ details.push(`unloaded models: ${unloaded.join(', ')}`);
489
+ }
490
+ throw new Error(
491
+ `Context dataset could not include the full import closure for '${modelUri}'. `
492
+ + `${details.join('; ')}`
493
+ );
494
+ }
495
+ }
496
+
497
+ async countContextDatasetQuads(modelUri: string): Promise<number> {
498
+ await this.ensureWorkspaceBuilt();
499
+ await this.ensureModelLoaded(modelUri);
500
+ await this.ensureContextDatasetReady(modelUri);
501
+ const store = this.reasoningStore.getStore();
502
+ const queryGraphs = this.resolveContextGraphs(modelUri);
503
+ let total = 0;
504
+ for (const graph of queryGraphs) {
505
+ total += store.countQuads(null, null, null, graph);
506
+ }
507
+ return total;
508
+ }
509
+
510
+ private resolveWorkspaceModelUri(uri: string): string | undefined {
511
+ const prepared = this.preparedModelAliases.get(uri);
512
+ if (prepared) {
513
+ return prepared;
514
+ }
515
+ if (isWorkspaceModelUri(uri)) {
516
+ return uri;
517
+ }
518
+ return this.ontologyModelIndex.resolveModelUri(uri);
519
+ }
520
+
521
+ private resolveLoadedDependencyOrder(modelUri: string): string[] {
522
+ const ordered = [...this.importGraph.dependenciesOf(modelUri), modelUri];
523
+ const seen = new Set<string>();
524
+ const resolved: string[] = [];
525
+ for (const uri of ordered) {
526
+ const model = this.resolveWorkspaceModelUri(uri);
527
+ if (!model || seen.has(model) || !this.isModelLoaded(model)) {
528
+ continue;
529
+ }
530
+ seen.add(model);
531
+ resolved.push(model);
532
+ }
533
+ return resolved;
534
+ }
535
+
536
+ private resolveContextGraphs(modelUri: string): NamedNode[] {
537
+ const seen = new Set<string>();
538
+ const graphs: NamedNode[] = [];
539
+ for (const uri of [...this.importGraph.dependenciesOf(modelUri), modelUri]) {
540
+ const modelGraphs = this.reasoningStore.graphs(uri);
541
+ if (!seen.has(modelGraphs.own.value)) {
542
+ seen.add(modelGraphs.own.value);
543
+ graphs.push(modelGraphs.own);
544
+ }
545
+ }
546
+ const ownGraphs = this.reasoningStore.graphs(modelUri);
547
+ if (!seen.has(ownGraphs.entailments.value)) {
548
+ seen.add(ownGraphs.entailments.value);
549
+ graphs.push(ownGraphs.entailments);
550
+ }
551
+ return graphs;
552
+ }
553
+
554
+ private isModelLoaded(modelUri: string): boolean {
555
+ try {
556
+ this.reasoningStore.graphs(modelUri);
557
+ return true;
558
+ } catch {
559
+ return false;
560
+ }
561
+ }
562
+
563
+ private ensureOwnTboxIndexesForDependencies(modelUri: string): void {
564
+ const ordered = this.resolveLoadedDependencyOrder(modelUri);
565
+ for (const uri of ordered) {
566
+ if (this.tboxIndexCache.hasOwn(uri)) {
567
+ continue;
568
+ }
569
+ const document = this.services.shared.workspace.LangiumDocuments.getDocument(URI.parse(uri));
570
+ if (!document) {
571
+ continue;
572
+ }
573
+ const ontology = document.parseResult.value;
574
+ if (!isVocabulary(ontology)) {
575
+ continue;
576
+ }
577
+ this.tboxChainer.chain(uri);
578
+ this.tboxIndexCache.setOwn(uri, this.tboxIndexBuilder.buildOwn(uri));
579
+ }
580
+ }
581
+
582
+ private notifySemanticChanged(modelUris: string[]): void {
583
+ const unique = [...new Set(modelUris)];
584
+ for (const listener of this.semanticChangeListeners) {
585
+ listener(unique);
586
+ }
587
+ }
588
+ }
589
+
590
+ export interface PreparedReasonedModel {
591
+ modelUri: string;
592
+ aliases?: ReadonlyArray<string>;
593
+ imports: ReadonlyArray<string>;
594
+ assertedQuads: ReadonlyArray<Quad>;
595
+ entailedQuads: ReadonlyArray<Quad>;
596
+ validationWarnings?: ReadonlyArray<string>;
597
+ }
598
+
599
+ function isWorkspaceModelUri(modelUri: string): boolean {
600
+ try {
601
+ const parsed = URI.parse(modelUri);
602
+ return parsed.path.toLowerCase().endsWith('.oml');
603
+ } catch {
604
+ return false;
605
+ }
606
+ }
607
+
608
+ const BUILT_IN_ONTOLOGIES = new Set([
609
+ 'http://www.w3.org/2001/XMLSchema',
610
+ 'http://www.w3.org/1999/02/22-rdf-syntax-ns',
611
+ 'http://www.w3.org/2000/01/rdf-schema',
612
+ 'http://www.w3.org/2002/07/owl',
613
+ 'http://www.w3.org/2003/11/swrlb',
614
+ ]);
615
+
616
+ function createDefaultDependencies(overrides: Partial<OwlReasoningDependencies>): OwlReasoningDependencies {
617
+ const mapper = overrides.mapper ?? new Oml2OwlMapper();
618
+ const reasoningStore = overrides.reasoningStore ?? new ReasoningStore();
619
+ const importGraph = overrides.importGraph ?? new ImportGraph();
620
+ const tboxIndexCache = overrides.tboxIndexCache ?? new TBoxIndexCache();
621
+ const tboxChainer = overrides.tboxChainer ?? new TBoxChainer(reasoningStore);
622
+ const tboxIndexBuilder = overrides.tboxIndexBuilder ?? new TBoxIndexBuilder(reasoningStore, tboxIndexCache, importGraph);
623
+ const aboxChainer = overrides.aboxChainer ?? new ABoxChainer(reasoningStore);
624
+ const aboxEntailmentCache = overrides.aboxEntailmentCache ?? new ABoxEntailmentCache();
625
+ const createSparqlService = overrides.createSparqlService ?? ((runner) => new SparqlService(
626
+ reasoningStore,
627
+ importGraph,
628
+ aboxEntailmentCache,
629
+ runner,
630
+ ));
631
+ return {
632
+ mapper,
633
+ reasoningStore,
634
+ importGraph,
635
+ tboxChainer,
636
+ tboxIndexBuilder,
637
+ tboxIndexCache,
638
+ aboxChainer,
639
+ aboxEntailmentCache,
640
+ createSparqlService,
641
+ };
642
+ }