@glossarist/concept-browser 0.4.12 → 0.4.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glossarist/concept-browser",
3
- "version": "0.4.12",
3
+ "version": "0.4.14",
4
4
  "description": "Vue SPA for browsing Glossarist terminology datasets with cross-reference resolution, graph visualization, and multi-language support",
5
5
  "type": "module",
6
6
  "bin": {
@@ -78,4 +78,4 @@
78
78
  "postcss.config.js",
79
79
  "env.d.ts"
80
80
  ]
81
- }
81
+ }
@@ -13,7 +13,9 @@ import { fileURLToPath } from 'url';
13
13
 
14
14
  const __dirname = dirname(fileURLToPath(import.meta.url));
15
15
  const ROOT = resolve(__dirname, '..');
16
- const ONTOLOGY_TTL = resolve(ROOT, '..', 'concept-model', 'ontologies', 'glossarist.ttl');
16
+ const CONCEPT_MODEL = resolve(ROOT, '..', 'concept-model', 'ontologies');
17
+ const ONTOLOGY_TTL = resolve(CONCEPT_MODEL, 'glossarist.ttl');
18
+ const SHACL_TTL = resolve(CONCEPT_MODEL, 'shapes', 'glossarist.shacl.ttl');
17
19
  const OUTPUT = resolve(ROOT, 'src', 'data', 'ontology-schema.json');
18
20
 
19
21
  const KNOWN_PREFIXES = {
@@ -27,6 +29,8 @@ const KNOWN_PREFIXES = {
27
29
  dcterms: 'http://purl.org/dc/terms/',
28
30
  prov: 'http://www.w3.org/ns/prov#',
29
31
  xsd: 'http://www.w3.org/2001/XMLSchema#',
32
+ sh: 'http://www.w3.org/ns/shacl#',
33
+ vann: 'http://purl.org/vocab/vann/',
30
34
  };
31
35
 
32
36
  function expandPrefixed(term) {
@@ -49,12 +53,14 @@ function compactIri(iri) {
49
53
 
50
54
  /**
51
55
  * Minimal TTL subject-block splitter. Handles nested [] and () and quoted strings.
56
+ * Tracks <...> URI references to avoid splitting on dots inside URIs.
52
57
  */
53
58
  function splitSubjectBlocks(text) {
54
59
  const blocks = [];
55
60
  let depth = 0;
56
61
  let start = -1;
57
62
  let inTripleQuote = false;
63
+ let inUri = false;
58
64
 
59
65
  for (let i = 0; i < text.length; i++) {
60
66
  const ch = text[i];
@@ -82,10 +88,20 @@ function splitSubjectBlocks(text) {
82
88
  continue;
83
89
  }
84
90
 
91
+ if (inUri) {
92
+ if (ch === '>') inUri = false;
93
+ continue;
94
+ }
95
+ if (ch === '<') {
96
+ inUri = true;
97
+ if (start < 0) start = i;
98
+ continue;
99
+ }
100
+
85
101
  if (ch === '[' || ch === '(') depth++;
86
102
  if (ch === ']' || ch === ')') depth--;
87
103
 
88
- if (depth === 0 && ch === '.') {
104
+ if (!inUri && depth === 0 && ch === '.') {
89
105
  if (start >= 0) {
90
106
  blocks.push(text.slice(start, i));
91
107
  start = -1;
@@ -141,10 +157,81 @@ function extractAllResources(block, predicate) {
141
157
  return [...new Set(results)];
142
158
  }
143
159
 
160
+ /**
161
+ * Strip @prefix and @base lines. Strip comments (#...) but preserve # inside <...> URIs
162
+ * and inside quoted strings.
163
+ */
164
+ function preprocessTtl(ttlText) {
165
+ const lines = ttlText.split('\n');
166
+ const filtered = lines.filter(l => !l.trimStart().startsWith('@prefix') && !l.trimStart().startsWith('@base'));
167
+ const text = filtered.join('\n');
168
+
169
+ // Character-by-character comment stripping
170
+ let result = '';
171
+ let inTriple = false;
172
+ let inSingle = false;
173
+ let inUri = false;
174
+
175
+ for (let i = 0; i < text.length; i++) {
176
+ const ch = text[i];
177
+
178
+ if (inTriple) {
179
+ result += ch;
180
+ if (ch === '"' && text.slice(i, i + 3) === '"""') {
181
+ result += text.slice(i + 1, i + 3);
182
+ i += 2;
183
+ inTriple = false;
184
+ }
185
+ continue;
186
+ }
187
+
188
+ if (inSingle) {
189
+ result += ch;
190
+ if (ch === '\\') { result += text[i + 1]; i++; continue; }
191
+ if (ch === '"') inSingle = false;
192
+ continue;
193
+ }
194
+
195
+ if (inUri) {
196
+ result += ch;
197
+ if (ch === '>') inUri = false;
198
+ continue;
199
+ }
200
+
201
+ if (ch === '"' && text.slice(i, i + 3) === '"""') {
202
+ result += '"""';
203
+ inTriple = true;
204
+ i += 2;
205
+ continue;
206
+ }
207
+
208
+ if (ch === '"') {
209
+ result += ch;
210
+ inSingle = true;
211
+ continue;
212
+ }
213
+
214
+ if (ch === '<') {
215
+ result += ch;
216
+ inUri = true;
217
+ continue;
218
+ }
219
+
220
+ if (ch === '#') {
221
+ // Skip until end of line
222
+ while (i < text.length && text[i] !== '\n') i++;
223
+ if (i < text.length) result += '\n';
224
+ continue;
225
+ }
226
+
227
+ result += ch;
228
+ }
229
+
230
+ return result;
231
+ }
232
+
144
233
  function parseOntology(ttlText) {
145
- const rawLines = ttlText.split('\n');
146
- // Remove comment lines but keep content
147
- const cleaned = rawLines.map(l => l.replace(/#[^\n]*/g, '')).join('\n');
234
+ const cleaned = preprocessTtl(ttlText);
148
235
 
149
236
  const blocks = splitSubjectBlocks(cleaned);
150
237
 
@@ -160,8 +247,7 @@ function parseOntology(ttlText) {
160
247
  if (!subjectMatch) continue;
161
248
  const subject = subjectMatch[1];
162
249
 
163
- // Skip ontology declaration, prefix declarations
164
- if (subject === '@prefix' || subject.startsWith('@')) continue;
250
+ // Skip ontology declaration
165
251
  if (subject.includes('glossarist>') && !subject.startsWith('gloss:')) continue;
166
252
 
167
253
  // Determine type
@@ -278,6 +364,176 @@ function groupPropertiesByDomain(properties) {
278
364
  return groups;
279
365
  }
280
366
 
367
+ /**
368
+ * Extract individual sh:property [...] constraint blocks from a shape block.
369
+ * Returns an array of strings, each being the inner content of one [ ... ].
370
+ */
371
+ function extractPropertyBlocks(shapeBlock) {
372
+ const blocks = [];
373
+ const re = /sh:property\s+\[/g;
374
+ let match;
375
+ while ((match = re.exec(shapeBlock)) !== null) {
376
+ const start = match.index + match[0].length - 1; // position of '['
377
+ let depth = 1;
378
+ let i = start + 1;
379
+ while (i < shapeBlock.length && depth > 0) {
380
+ if (shapeBlock[i] === '[') depth++;
381
+ else if (shapeBlock[i] === ']') depth--;
382
+ i++;
383
+ }
384
+ blocks.push(shapeBlock.slice(start + 1, i - 1));
385
+ }
386
+ return blocks;
387
+ }
388
+
389
+ function parseConstraint(propBlock) {
390
+ const c = {};
391
+ const path = extractResource(propBlock, 'sh:path');
392
+ c.path = path ? compactIri(expandPrefixed(path)) : null;
393
+
394
+ const dt = extractResource(propBlock, 'sh:datatype');
395
+ c.datatype = dt ? compactIri(expandPrefixed(dt)) : null;
396
+
397
+ const cls = extractResource(propBlock, 'sh:class');
398
+ c.class = cls ? compactIri(expandPrefixed(cls)) : null;
399
+
400
+ const vf = extractResource(propBlock, 'sh:valuesFrom');
401
+ c.valuesFrom = vf ? compactIri(expandPrefixed(vf)) : null;
402
+
403
+ const nk = extractResource(propBlock, 'sh:nodeKind');
404
+ c.nodeKind = nk ? compactIri(expandPrefixed(nk)) : null;
405
+
406
+ const minMatch = propBlock.match(/sh:minCount\s+(\d+)/);
407
+ c.minCount = minMatch ? parseInt(minMatch[1], 10) : null;
408
+
409
+ const maxMatch = propBlock.match(/sh:maxCount\s+(\d+)/);
410
+ c.maxCount = maxMatch ? parseInt(maxMatch[1], 10) : null;
411
+
412
+ // sh:in ( "val1" "val2" ... )
413
+ const inMatch = propBlock.match(/sh:in\s*\(([^)]+)\)/);
414
+ if (inMatch) {
415
+ c.in = inMatch[1].match(/"([^"]+)"/g)?.map(s => s.replace(/"/g, '')) || null;
416
+ } else {
417
+ c.in = null;
418
+ }
419
+
420
+ return c;
421
+ }
422
+
423
+ function parseShaclShapes(ttlText) {
424
+ const cleaned = preprocessTtl(ttlText);
425
+ const blocks = splitSubjectBlocks(cleaned);
426
+
427
+ const shapes = [];
428
+ for (const block of blocks) {
429
+ const trimmed = block.trim();
430
+ if (!trimmed) continue;
431
+
432
+ const subjectMatch = trimmed.match(/^([^\s]+)/);
433
+ if (!subjectMatch) continue;
434
+ const subject = subjectMatch[1];
435
+
436
+ // Check if this is a sh:NodeShape
437
+ if (!/\ba\s+sh:NodeShape\b/.test(trimmed)) continue;
438
+
439
+ const iri = expandPrefixed(subject);
440
+ const compact = compactIri(iri);
441
+ const label = extractLiteral(trimmed, 'rdfs:label');
442
+ const comment = extractLiteral(trimmed, 'rdfs:comment');
443
+ const targetClass = extractResource(trimmed, 'sh:targetClass');
444
+
445
+ const propBlocks = extractPropertyBlocks(trimmed);
446
+ const constraints = propBlocks.map(parseConstraint).filter(c => c.path);
447
+
448
+ // sh:class at shape level (not inside sh:property blocks)
449
+ const shapeClassMatch = trimmed.match(/^\s*[^[]*?\ba\s+sh:NodeShape\s[^[]*?sh:class\s+([^\s,;]+)/);
450
+ const shapeClass = shapeClassMatch ? shapeClassMatch[1].replace(/[;.]+$/, '') : null;
451
+
452
+ shapes.push({
453
+ iri,
454
+ compact,
455
+ label: label || subject.replace('gloss:', '').replace('Shape', ''),
456
+ comment,
457
+ targetClass: targetClass ? compactIri(expandPrefixed(targetClass)) : null,
458
+ shapeClass: shapeClass ? compactIri(expandPrefixed(shapeClass)) : null,
459
+ constraints,
460
+ });
461
+ }
462
+
463
+ return shapes;
464
+ }
465
+
466
+ const IMPORT_LABELS = {
467
+ 'http://www.w3.org/2004/02/skos/core': 'SKOS',
468
+ 'http://www.w3.org/2004/02/skos/core#': 'SKOS',
469
+ 'http://www.w3.org/2008/05/skos-xl': 'SKOS-XL',
470
+ 'http://www.w3.org/2008/05/skos-xl#': 'SKOS-XL',
471
+ 'http://purl.org/iso25964/skos-thes': 'ISO 25964',
472
+ 'http://purl.org/iso25964/skos-thes#': 'ISO 25964',
473
+ 'http://www.w3.org/ns/prov#': 'PROV-O',
474
+ 'http://purl.org/dc/terms/': 'Dublin Core Terms',
475
+ };
476
+
477
+ function parseOntologyDeclaration(ttlText) {
478
+ const cleaned = preprocessTtl(ttlText);
479
+ const blocks = splitSubjectBlocks(cleaned);
480
+
481
+ for (const block of blocks) {
482
+ const trimmed = block.trim();
483
+ if (!trimmed) continue;
484
+
485
+ // Match the owl:Ontology declaration
486
+ if (!/\ba\s+owl:Ontology\b/.test(trimmed)) continue;
487
+
488
+ const subjectMatch = trimmed.match(/^<([^>]+)>/);
489
+ const iri = subjectMatch ? subjectMatch[1] : null;
490
+ const label = extractLiteral(trimmed, 'rdfs:label') || extractLiteral(trimmed, 'dcterms:title');
491
+ const comment = extractLiteral(trimmed, 'rdfs:comment') || extractLiteral(trimmed, 'dcterms:description');
492
+ const prefix = extractLiteral(trimmed, 'vann:preferredNamespacePrefix');
493
+ const nsUri = extractLiteral(trimmed, 'vann:preferredNamespaceUri');
494
+ const license = extractResource(trimmed, 'dcterms:license')?.replace(/[<>]/g, '') || null;
495
+ const created = extractLiteral(trimmed, 'dcterms:created');
496
+
497
+ const imports = [];
498
+ const importRe = /owl:imports\s+<([^>]+)>/g;
499
+ let im;
500
+ while ((im = importRe.exec(trimmed)) !== null) {
501
+ const iri = im[1];
502
+ imports.push({
503
+ iri,
504
+ label: IMPORT_LABELS[iri] || IMPORT_LABELS[iri.replace(/#?$/, '#')] || compactIri(iri),
505
+ });
506
+ }
507
+
508
+ return { iri, label, comment, prefix, namespaceUri: nsUri, imports, license, created };
509
+ }
510
+ return null;
511
+ }
512
+
513
+ function parseAnnotationProperties(ttlText) {
514
+ const cleaned = preprocessTtl(ttlText);
515
+ const blocks = splitSubjectBlocks(cleaned);
516
+
517
+ const props = [];
518
+ for (const block of blocks) {
519
+ const trimmed = block.trim();
520
+ if (!trimmed) continue;
521
+
522
+ if (!/\ba\s+owl:AnnotationProperty\b/.test(trimmed)) continue;
523
+
524
+ const subjectMatch = trimmed.match(/^([^\s]+)/);
525
+ if (!subjectMatch) continue;
526
+ const subject = subjectMatch[1];
527
+ const iri = expandPrefixed(subject);
528
+ const compact = compactIri(iri);
529
+ const label = extractLiteral(trimmed, 'rdfs:label');
530
+
531
+ props.push({ iri, compact, label: label || compact });
532
+ }
533
+
534
+ return props;
535
+ }
536
+
281
537
  function main() {
282
538
  if (!existsSync(ONTOLOGY_TTL)) {
283
539
  console.error(`Ontology file not found: ${ONTOLOGY_TTL}`);
@@ -291,24 +547,70 @@ function main() {
291
547
  const hierarchy = buildClassHierarchy(classes);
292
548
  const propsByDomain = groupPropertiesByDomain(properties);
293
549
 
550
+ let shapes = [];
551
+ if (existsSync(SHACL_TTL)) {
552
+ const shaclText = readFileSync(SHACL_TTL, 'utf-8');
553
+ shapes = parseShaclShapes(shaclText);
554
+ } else {
555
+ console.warn(`SHACL shapes file not found: ${SHACL_TTL}`);
556
+ }
557
+
558
+ const ontologyDecl = parseOntologyDeclaration(ttlText);
559
+ let annotationProps = parseAnnotationProperties(ttlText);
560
+
561
+ // Hardcode standard annotation properties used in the ontology (not declared as owl:AnnotationProperty)
562
+ if (annotationProps.length === 0) {
563
+ annotationProps = [
564
+ { iri: 'http://www.w3.org/2000/01/rdf-schema#label', compact: 'rdfs:label', label: 'label' },
565
+ { iri: 'http://www.w3.org/2000/01/rdf-schema#comment', compact: 'rdfs:comment', label: 'comment' },
566
+ { iri: 'http://www.w3.org/2000/01/rdf-schema#seeAlso', compact: 'rdfs:seeAlso', label: 'seeAlso' },
567
+ { iri: 'http://www.w3.org/2000/01/rdf-schema#isDefinedBy', compact: 'rdfs:isDefinedBy', label: 'isDefinedBy' },
568
+ { iri: 'http://purl.org/dc/terms/title', compact: 'dcterms:title', label: 'title' },
569
+ { iri: 'http://purl.org/dc/terms/description', compact: 'dcterms:description', label: 'description' },
570
+ { iri: 'http://purl.org/dc/terms/source', compact: 'dcterms:source', label: 'source' },
571
+ { iri: 'http://purl.org/dc/terms/license', compact: 'dcterms:license', label: 'license' },
572
+ { iri: 'http://purl.org/dc/terms/created', compact: 'dcterms:created', label: 'created' },
573
+ { iri: 'http://purl.org/vocab/vann/preferredNamespacePrefix', compact: 'vann:preferredNamespacePrefix', label: 'preferredNamespacePrefix' },
574
+ { iri: 'http://purl.org/vocab/vann/preferredNamespaceUri', compact: 'vann:preferredNamespaceUri', label: 'preferredNamespaceUri' },
575
+ ];
576
+ }
577
+
578
+ // Group shapes by targetClass
579
+ const shapesByTargetClass = {};
580
+ for (const s of shapes) {
581
+ const tc = s.targetClass || '(unspecified)';
582
+ if (!shapesByTargetClass[tc]) shapesByTargetClass[tc] = [];
583
+ shapesByTargetClass[tc].push(s.compact);
584
+ }
585
+
294
586
  const output = {
295
- ontologyIri: 'https://www.glossarist.org/ontologies/glossarist',
296
- ontologyLabel: 'Glossarist Ontology',
587
+ ontology: ontologyDecl,
588
+ ontologyIri: ontologyDecl?.iri || 'https://www.glossarist.org/ontologies/glossarist',
589
+ ontologyLabel: ontologyDecl?.label || 'Glossarist Ontology',
297
590
  classes: hierarchy.map,
298
591
  classHierarchyRoots: hierarchy.roots,
299
592
  properties: Object.fromEntries(properties.map(p => [p.compact, p])),
300
593
  propertiesByDomain: propsByDomain,
594
+ shapes: Object.fromEntries(shapes.map(s => [s.compact, s])),
595
+ shapesByTargetClass,
596
+ annotationProperties: annotationProps,
301
597
  stats: {
302
598
  classCount: classes.length,
303
599
  objectPropertyCount: properties.filter(p => p.type === 'object').length,
304
600
  datatypePropertyCount: properties.filter(p => p.type === 'datatype').length,
601
+ shapeCount: shapes.length,
602
+ annotationPropertyCount: annotationProps.length,
305
603
  },
306
604
  };
307
605
 
308
606
  mkdirSync(dirname(OUTPUT), { recursive: true });
309
607
  writeFileSync(OUTPUT, JSON.stringify(output, null, 2) + '\n');
310
608
 
311
- console.log(`Parsed ${output.stats.classCount} classes, ${output.stats.objectPropertyCount} object properties, ${output.stats.datatypePropertyCount} datatype properties`);
609
+ console.log(`Parsed ${output.stats.classCount} classes, ${output.stats.objectPropertyCount} object properties, ${output.stats.datatypePropertyCount} datatype properties, ${output.stats.shapeCount} SHACL shapes, ${output.stats.annotationPropertyCount} annotation properties`);
610
+ if (ontologyDecl) {
611
+ console.log(`Ontology: ${ontologyDecl.iri}`);
612
+ console.log(` imports: ${ontologyDecl.imports.map(i => i.label).join(', ')}`);
613
+ }
312
614
  console.log(`Wrote ${OUTPUT}`);
313
615
  }
314
616
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Ontology schema loader — provides class/property definitions parsed from
2
+ * Ontology schema loader — provides class/property/shape definitions parsed from
3
3
  * the Glossarist OWL ontology for the Ontospy-style concept view.
4
4
  */
5
5
  import schemaData from '../data/ontology-schema.json';
@@ -28,14 +28,72 @@ export interface OwlProperty {
28
28
  inverseOf: string | null;
29
29
  }
30
30
 
31
+ export interface ShaclConstraint {
32
+ path: string | null;
33
+ datatype: string | null;
34
+ class: string | null;
35
+ valuesFrom: string | null;
36
+ nodeKind: string | null;
37
+ minCount: number | null;
38
+ maxCount: number | null;
39
+ in: string[] | null;
40
+ }
41
+
42
+ export interface OwlShape {
43
+ iri: string;
44
+ compact: string;
45
+ label: string;
46
+ comment: string | null;
47
+ targetClass: string | null;
48
+ shapeClass: string | null;
49
+ constraints: ShaclConstraint[];
50
+ }
51
+
52
+ export interface OwlOntology {
53
+ iri: string;
54
+ label: string;
55
+ comment: string | null;
56
+ prefix: string | null;
57
+ namespaceUri: string | null;
58
+ imports: { iri: string; label: string }[];
59
+ license: string | null;
60
+ created: string | null;
61
+ }
62
+
63
+ export interface AnnotationProperty {
64
+ iri: string;
65
+ compact: string;
66
+ label: string;
67
+ }
68
+
69
+ export type EntityType = 'class' | 'objectProperty' | 'datatypeProperty' | 'shape' | 'annotationProperty';
70
+
71
+ export const ENTITY_TYPE_META: Record<EntityType, { label: string; color: string }> = {
72
+ class: { label: 'Classes', color: 'blue' },
73
+ objectProperty: { label: 'Object Properties', color: 'emerald' },
74
+ datatypeProperty: { label: 'Datatype Properties', color: 'amber' },
75
+ shape: { label: 'SHACL Shapes', color: 'purple' },
76
+ annotationProperty: { label: 'Annotation Properties', color: 'pink' },
77
+ };
78
+
31
79
  interface OntologySchema {
80
+ ontology: OwlOntology | null;
32
81
  ontologyIri: string;
33
82
  ontologyLabel: string;
34
83
  classes: Record<string, OwlClass>;
35
84
  classHierarchyRoots: string[];
36
85
  properties: Record<string, OwlProperty>;
37
86
  propertiesByDomain: Record<string, { object: string[]; datatype: string[] }>;
38
- stats: { classCount: number; objectPropertyCount: number; datatypePropertyCount: number };
87
+ shapes: Record<string, OwlShape>;
88
+ shapesByTargetClass: Record<string, string[]>;
89
+ annotationProperties: AnnotationProperty[];
90
+ stats: {
91
+ classCount: number;
92
+ objectPropertyCount: number;
93
+ datatypePropertyCount: number;
94
+ shapeCount: number;
95
+ annotationPropertyCount: number;
96
+ };
39
97
  }
40
98
 
41
99
  const data = schemaData as unknown as OntologySchema;
@@ -48,6 +106,10 @@ export function getProperty(id: string): OwlProperty | null {
48
106
  return data.properties[id] ?? null;
49
107
  }
50
108
 
109
+ export function getShape(id: string): OwlShape | null {
110
+ return data.shapes[id] ?? null;
111
+ }
112
+
51
113
  export function getPropertiesForDomain(domain: string): { object: OwlProperty[]; datatype: OwlProperty[] } {
52
114
  const group = data.propertiesByDomain[domain];
53
115
  if (!group) return { object: [], datatype: [] };
@@ -57,7 +119,6 @@ export function getPropertiesForDomain(domain: string): { object: OwlProperty[];
57
119
  };
58
120
  }
59
121
 
60
- /** Get all properties applicable to a class, including inherited ones. */
61
122
  export function getAllPropertiesForClass(classId: string): { object: OwlProperty[]; datatype: OwlProperty[] } {
62
123
  const cls = data.classes[classId];
63
124
  if (!cls) return { object: [], datatype: [] };
@@ -80,7 +141,11 @@ export function getAllPropertiesForClass(classId: string): { object: OwlProperty
80
141
  return { object: objectProps, datatype: datatypeProps };
81
142
  }
82
143
 
83
- /** Get the full class hierarchy tree starting from roots. */
144
+ export function getShapesForClass(classId: string): OwlShape[] {
145
+ const shapeIds = data.shapesByTargetClass[classId] ?? [];
146
+ return shapeIds.map(id => data.shapes[id]).filter(Boolean);
147
+ }
148
+
84
149
  export function getClassTree(): OwlClass[] {
85
150
  return data.classHierarchyRoots
86
151
  .map(id => data.classes[id])
@@ -95,6 +160,26 @@ export function getAllProperties(): OwlProperty[] {
95
160
  return Object.values(data.properties);
96
161
  }
97
162
 
163
+ export function getObjectProperties(): OwlProperty[] {
164
+ return Object.values(data.properties).filter(p => p.type === 'object');
165
+ }
166
+
167
+ export function getDatatypeProperties(): OwlProperty[] {
168
+ return Object.values(data.properties).filter(p => p.type === 'datatype');
169
+ }
170
+
171
+ export function getAllShapes(): OwlShape[] {
172
+ return Object.values(data.shapes);
173
+ }
174
+
175
+ export function getAnnotationProperties(): AnnotationProperty[] {
176
+ return data.annotationProperties;
177
+ }
178
+
179
+ export function getOntology(): OwlOntology | null {
180
+ return data.ontology;
181
+ }
182
+
98
183
  export function getStats() {
99
184
  return data.stats;
100
185
  }