@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.
- package/README.md +46 -0
- package/out/index.d.ts +10 -0
- package/out/index.js +12 -0
- package/out/index.js.map +1 -0
- package/out/owl/owl-abox.d.ts +79 -0
- package/out/owl/owl-abox.js +765 -0
- package/out/owl/owl-abox.js.map +1 -0
- package/out/owl/owl-imports.d.ts +9 -0
- package/out/owl/owl-imports.js +102 -0
- package/out/owl/owl-imports.js.map +1 -0
- package/out/owl/owl-interfaces.d.ts +121 -0
- package/out/owl/owl-interfaces.js +3 -0
- package/out/owl/owl-interfaces.js.map +1 -0
- package/out/owl/owl-mapper.d.ts +80 -0
- package/out/owl/owl-mapper.js +1217 -0
- package/out/owl/owl-mapper.js.map +1 -0
- package/out/owl/owl-service.d.ts +65 -0
- package/out/owl/owl-service.js +552 -0
- package/out/owl/owl-service.js.map +1 -0
- package/out/owl/owl-shacl.d.ts +28 -0
- package/out/owl/owl-shacl.js +337 -0
- package/out/owl/owl-shacl.js.map +1 -0
- package/out/owl/owl-sparql.d.ts +71 -0
- package/out/owl/owl-sparql.js +260 -0
- package/out/owl/owl-sparql.js.map +1 -0
- package/out/owl/owl-store.d.ts +32 -0
- package/out/owl/owl-store.js +142 -0
- package/out/owl/owl-store.js.map +1 -0
- package/out/owl/owl-tbox.d.ts +98 -0
- package/out/owl/owl-tbox.js +575 -0
- package/out/owl/owl-tbox.js.map +1 -0
- package/out/owl-module.d.ts +15 -0
- package/out/owl-module.js +22 -0
- package/out/owl-module.js.map +1 -0
- package/package.json +52 -0
- package/src/index.ts +12 -0
- package/src/owl/owl-abox.ts +930 -0
- package/src/owl/owl-imports.ts +108 -0
- package/src/owl/owl-interfaces.ts +145 -0
- package/src/owl/owl-mapper.ts +1510 -0
- package/src/owl/owl-service.ts +642 -0
- package/src/owl/owl-shacl.ts +400 -0
- package/src/owl/owl-sparql.ts +317 -0
- package/src/owl/owl-store.ts +173 -0
- package/src/owl/owl-tbox.ts +727 -0
- package/src/owl-module.ts +52 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
// Copyright (c) 2026 Modelware. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import type { Quad, Term } from '@rdfjs/types';
|
|
4
|
+
import { Parser as N3Parser, Store as N3Store } from 'n3';
|
|
5
|
+
import SHACLValidator from 'rdf-validate-shacl';
|
|
6
|
+
import type {
|
|
7
|
+
OwlShaclService,
|
|
8
|
+
OwlShaclValidationIssue,
|
|
9
|
+
OwlShaclValidationResult,
|
|
10
|
+
OwlSparqlService,
|
|
11
|
+
} from './owl-interfaces.js';
|
|
12
|
+
|
|
13
|
+
export const ShaclValidateRequest = 'oml/table-editor/validate';
|
|
14
|
+
|
|
15
|
+
export interface ShaclValidateParams {
|
|
16
|
+
modelUri?: string;
|
|
17
|
+
shaclSource: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type ShaclValidationIssue = OwlShaclValidationIssue;
|
|
21
|
+
export type ShaclValidationResult = OwlShaclValidationResult;
|
|
22
|
+
export type ShaclValidateResult = ShaclValidationResult;
|
|
23
|
+
export type DerivedShaclSelectQuery = {
|
|
24
|
+
sparql: string;
|
|
25
|
+
columnVariables: string[];
|
|
26
|
+
columnLabels: Map<string, string>;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
type ConnectionLike = {
|
|
30
|
+
onRequest: (type: string, handler: (params: ShaclValidateParams) => ShaclValidateResult | Promise<ShaclValidateResult>) => void;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export class ShaclService implements OwlShaclService {
|
|
34
|
+
constructor(
|
|
35
|
+
private readonly sparqlService: OwlSparqlService,
|
|
36
|
+
private readonly prepareContext: (modelUri: string) => Promise<void>,
|
|
37
|
+
private readonly resolveContextIri: (modelUri: string) => string,
|
|
38
|
+
) {}
|
|
39
|
+
|
|
40
|
+
async validateShacl(modelUri: string | undefined, shaclSource: string): Promise<ShaclValidationResult> {
|
|
41
|
+
if (!modelUri) {
|
|
42
|
+
return {
|
|
43
|
+
conforms: false,
|
|
44
|
+
issues: [],
|
|
45
|
+
error: 'No contextUri model is available for this markdown document.',
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const trimmedSource = stripShaclFrontMatter(shaclSource).trim();
|
|
50
|
+
if (!trimmedSource) {
|
|
51
|
+
return {
|
|
52
|
+
conforms: false,
|
|
53
|
+
issues: [],
|
|
54
|
+
error: 'Enter a shape to validate.',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let shapeQuads;
|
|
59
|
+
try {
|
|
60
|
+
const parser = new N3Parser();
|
|
61
|
+
shapeQuads = parser.parse(trimmedSource);
|
|
62
|
+
if (shapeQuads.length === 0) {
|
|
63
|
+
return {
|
|
64
|
+
conforms: false,
|
|
65
|
+
issues: [],
|
|
66
|
+
error: 'No content found.',
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
} catch (error) {
|
|
70
|
+
return {
|
|
71
|
+
conforms: false,
|
|
72
|
+
issues: [],
|
|
73
|
+
error: error instanceof Error ? error.message : 'Invalid SHACL shapes.',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
await this.prepareContext(modelUri);
|
|
78
|
+
const contextIri = this.resolveContextIri(modelUri);
|
|
79
|
+
const constructResult = await this.sparqlService.construct(modelUri, buildValidationConstructQuery(contextIri));
|
|
80
|
+
if (!constructResult.success) {
|
|
81
|
+
return {
|
|
82
|
+
conforms: false,
|
|
83
|
+
issues: [],
|
|
84
|
+
error: constructResult.error ?? 'SPARQL CONSTRUCT failed for validation.',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const propertyNameByPath = collectShaclPropertyDisplayNames(shapeQuads);
|
|
90
|
+
const shapesDataset = new N3Store(shapeQuads);
|
|
91
|
+
const dataDataset = new N3Store(constructResult.quads);
|
|
92
|
+
const validator = new SHACLValidator(shapesDataset);
|
|
93
|
+
const report = await validator.validate(dataDataset);
|
|
94
|
+
const rawResults = Array.isArray((report as any)?.results) ? (report as any).results : [];
|
|
95
|
+
const issues: ShaclValidationIssue[] = rawResults.map((result: any) => {
|
|
96
|
+
const path = formatValidationField(result?.path);
|
|
97
|
+
return {
|
|
98
|
+
message: formatValidationField(result?.message) || 'Validation issue.',
|
|
99
|
+
focusNode: formatValidationField(result?.focusNode),
|
|
100
|
+
path,
|
|
101
|
+
propertyName: path ? propertyNameByPath.get(path) : undefined,
|
|
102
|
+
severity: formatValidationField(result?.severity),
|
|
103
|
+
source: formatValidationField(result?.sourceConstraintComponent ?? result?.sourceShape),
|
|
104
|
+
};
|
|
105
|
+
});
|
|
106
|
+
return {
|
|
107
|
+
conforms: Boolean((report as any)?.conforms),
|
|
108
|
+
issues,
|
|
109
|
+
};
|
|
110
|
+
} catch (error) {
|
|
111
|
+
return {
|
|
112
|
+
conforms: false,
|
|
113
|
+
issues: [],
|
|
114
|
+
error: error instanceof Error ? error.message : 'Validation failed.',
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function createDefaultShaclService(
|
|
121
|
+
sparqlService: OwlSparqlService,
|
|
122
|
+
prepareContext: (modelUri: string) => Promise<void>,
|
|
123
|
+
resolveContextIri: (modelUri: string) => string,
|
|
124
|
+
): OwlShaclService {
|
|
125
|
+
return new ShaclService(sparqlService, prepareContext, resolveContextIri);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function registerShaclValidationRequests(connection: ConnectionLike, oml: any): void {
|
|
129
|
+
connection.onRequest(ShaclValidateRequest, async (params: ShaclValidateParams): Promise<ShaclValidateResult> => {
|
|
130
|
+
const reasoningService = oml?.reasoning?.ReasoningService;
|
|
131
|
+
if (!reasoningService
|
|
132
|
+
|| typeof reasoningService.getSparqlService !== 'function'
|
|
133
|
+
|| typeof reasoningService.ensureQueryContext !== 'function'
|
|
134
|
+
|| typeof reasoningService.getContextIri !== 'function') {
|
|
135
|
+
return {
|
|
136
|
+
conforms: false,
|
|
137
|
+
issues: [],
|
|
138
|
+
error: 'Reasoning service validation is unavailable.',
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
const modelUri = typeof params?.modelUri === 'string' ? params.modelUri : undefined;
|
|
142
|
+
const shaclSource = typeof params?.shaclSource === 'string' ? params.shaclSource : '';
|
|
143
|
+
const shaclService = createDefaultShaclService(
|
|
144
|
+
reasoningService.getSparqlService(),
|
|
145
|
+
(uri) => reasoningService.ensureQueryContext(uri),
|
|
146
|
+
(uri) => reasoningService.getContextIri(uri),
|
|
147
|
+
);
|
|
148
|
+
return shaclService.validateShacl(modelUri, shaclSource);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function deriveSelectQueryFromShacl(shaclSource: string, graphIri: string): DerivedShaclSelectQuery | undefined {
|
|
153
|
+
let shapesStore: N3Store;
|
|
154
|
+
try {
|
|
155
|
+
const parser = new N3Parser({ format: 'text/turtle' });
|
|
156
|
+
const quads = parser.parse(shaclSource);
|
|
157
|
+
shapesStore = new N3Store(quads);
|
|
158
|
+
} catch {
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const validator = new SHACLValidator(shapesStore);
|
|
163
|
+
const { sh } = validator.ns;
|
|
164
|
+
const shapesGraph = validator.shapesGraph;
|
|
165
|
+
const nodeShape = shapesGraph.shapesWithTarget.find((shape: any) => !shape.isPropertyShape && !shape.deactivated);
|
|
166
|
+
if (!nodeShape) {
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const targetClassTerms = nodeShape.shapeNodePointer.out(sh.targetClass).terms as Term[];
|
|
171
|
+
const targetClasses = targetClassTerms.filter((term: Term): term is Term => term.termType === 'NamedNode');
|
|
172
|
+
const targetNodes = nodeShape.shapeNodePointer.out(sh.targetNode).terms as Term[];
|
|
173
|
+
if (targetClasses.length === 0 && targetNodes.length === 0) {
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const propertyShapeTerms = nodeShape.shapeNodePointer.out(sh.property).terms as Term[];
|
|
178
|
+
const columns: Array<{ path: Term; name?: string }> = [];
|
|
179
|
+
for (const propertyShapeTerm of propertyShapeTerms) {
|
|
180
|
+
const propertyShape = shapesGraph.getShape(propertyShapeTerm);
|
|
181
|
+
if (propertyShape.deactivated) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
const path = propertyShape.pathObject;
|
|
185
|
+
if (!isRdfTerm(path) || path.termType !== 'NamedNode') {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const nameTerm = propertyShape.shapeNodePointer.out(sh.name).term;
|
|
189
|
+
const name = nameTerm && nameTerm.termType === 'Literal' ? nameTerm.value : undefined;
|
|
190
|
+
columns.push({ path, name });
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const usedNames = new Set<string>(['focus']);
|
|
194
|
+
const variables = columns.map((column) => {
|
|
195
|
+
const base = normalizeSparqlVariableName(column.name || localName(column.path.value) || 'value');
|
|
196
|
+
const unique = uniqueVariableName(base, usedNames);
|
|
197
|
+
usedNames.add(unique);
|
|
198
|
+
const sourceVariable = uniqueVariableName(`__${unique}`, usedNames);
|
|
199
|
+
usedNames.add(sourceVariable);
|
|
200
|
+
return {
|
|
201
|
+
path: column.path,
|
|
202
|
+
variable: unique,
|
|
203
|
+
sourceVariable,
|
|
204
|
+
name: column.name
|
|
205
|
+
};
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const selectVars = [
|
|
209
|
+
'?focus',
|
|
210
|
+
...variables.map((entry: { variable: string; sourceVariable: string }) =>
|
|
211
|
+
`(GROUP_CONCAT(DISTINCT STR(?${entry.sourceVariable}); SEPARATOR=", ") AS ?${entry.variable})`
|
|
212
|
+
)
|
|
213
|
+
].join(' ');
|
|
214
|
+
const whereLines: string[] = [];
|
|
215
|
+
const graphLines: string[] = [];
|
|
216
|
+
|
|
217
|
+
if (targetClasses.length > 0) {
|
|
218
|
+
const values = targetClasses.map((term: Term) => `<${term.value}>`).join(' ');
|
|
219
|
+
whereLines.push(` VALUES ?__targetClass { ${values} }`);
|
|
220
|
+
graphLines.push(' ?focus a ?__targetClass .');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (targetNodes.length > 0) {
|
|
224
|
+
const values = targetNodes.map((term: Term) => termToSparql(term)).join(' ');
|
|
225
|
+
whereLines.push(` VALUES ?focus { ${values} }`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
for (const entry of variables) {
|
|
229
|
+
graphLines.push(` OPTIONAL { ?focus <${entry.path.value}> ?${entry.sourceVariable} . }`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
whereLines.push(` GRAPH <${escapeIriForSparql(graphIri)}> {`);
|
|
233
|
+
whereLines.push(...graphLines);
|
|
234
|
+
whereLines.push(' }');
|
|
235
|
+
|
|
236
|
+
const columnVariables = ['focus', ...variables.map((entry) => entry.variable)];
|
|
237
|
+
const columnLabels = new Map<string, string>([['focus', 'focus']]);
|
|
238
|
+
for (const variable of variables) {
|
|
239
|
+
columnLabels.set(variable.variable, variable.name || variable.variable);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
sparql: [
|
|
244
|
+
`SELECT ${selectVars}`,
|
|
245
|
+
'WHERE {',
|
|
246
|
+
...whereLines,
|
|
247
|
+
'}',
|
|
248
|
+
'GROUP BY ?focus',
|
|
249
|
+
].join('\n'),
|
|
250
|
+
columnVariables,
|
|
251
|
+
columnLabels,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function buildValidationConstructQuery(contextIri: string): string {
|
|
256
|
+
return [
|
|
257
|
+
'CONSTRUCT {',
|
|
258
|
+
' ?s ?p ?o .',
|
|
259
|
+
' ?s a ?sType .',
|
|
260
|
+
' ?o a ?oType .',
|
|
261
|
+
'}',
|
|
262
|
+
'WHERE {',
|
|
263
|
+
` GRAPH <${contextIri}> { ?s ?p ?o }`,
|
|
264
|
+
` OPTIONAL { GRAPH ?g1 { ?s a ?sType } FILTER(?g1 != <${contextIri}>) }`,
|
|
265
|
+
' OPTIONAL {',
|
|
266
|
+
' FILTER(isIRI(?o) || isBlank(?o))',
|
|
267
|
+
` GRAPH ?g2 { ?o a ?oType } FILTER(?g2 != <${contextIri}>)`,
|
|
268
|
+
' }',
|
|
269
|
+
'}',
|
|
270
|
+
].join('\n');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function formatValidationField(value: unknown): string | undefined {
|
|
274
|
+
if (value === undefined || value === null) return undefined;
|
|
275
|
+
if (Array.isArray(value)) {
|
|
276
|
+
const parts = value
|
|
277
|
+
.map((entry) => formatValidationField(entry))
|
|
278
|
+
.filter((entry): entry is string => Boolean(entry));
|
|
279
|
+
return parts.join('; ');
|
|
280
|
+
}
|
|
281
|
+
if (typeof value === 'string') return value;
|
|
282
|
+
if (typeof value === 'number' || typeof value === 'boolean') return String(value);
|
|
283
|
+
if (typeof value === 'object' && 'value' in (value as any)) {
|
|
284
|
+
const raw = (value as any).value;
|
|
285
|
+
return typeof raw === 'string' ? raw : String(raw);
|
|
286
|
+
}
|
|
287
|
+
return String(value);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function collectShaclPropertyDisplayNames(shapeQuads: readonly Quad[]): Map<string, string> {
|
|
291
|
+
const SH_PATH = 'http://www.w3.org/ns/shacl#path';
|
|
292
|
+
const SH_NAME = 'http://www.w3.org/ns/shacl#name';
|
|
293
|
+
const pathByPropertyShape = new Map<string, string>();
|
|
294
|
+
const nameByPropertyShape = new Map<string, string>();
|
|
295
|
+
|
|
296
|
+
const shapeKey = (term: Term): string | undefined => {
|
|
297
|
+
if (term.termType !== 'NamedNode' && term.termType !== 'BlankNode') {
|
|
298
|
+
return undefined;
|
|
299
|
+
}
|
|
300
|
+
return `${term.termType}:${term.value}`;
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
for (const quad of shapeQuads) {
|
|
304
|
+
const key = shapeKey(quad.subject);
|
|
305
|
+
if (!key) {
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
if (quad.predicate.value === SH_PATH && quad.object.termType === 'NamedNode') {
|
|
309
|
+
pathByPropertyShape.set(key, quad.object.value);
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (quad.predicate.value === SH_NAME && quad.object.termType === 'Literal') {
|
|
313
|
+
const label = quad.object.value.trim();
|
|
314
|
+
if (label.length > 0) {
|
|
315
|
+
nameByPropertyShape.set(key, label);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const result = new Map<string, string>();
|
|
321
|
+
for (const [key, path] of pathByPropertyShape.entries()) {
|
|
322
|
+
const name = nameByPropertyShape.get(key);
|
|
323
|
+
if (name && !result.has(path)) {
|
|
324
|
+
result.set(path, name);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return result;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function stripShaclFrontMatter(raw: string): string {
|
|
331
|
+
let content = raw;
|
|
332
|
+
const frontmatterRegex = /^---\s*[\r\n]+([\s\S]*?)[\r\n]+---\s*/;
|
|
333
|
+
const match = frontmatterRegex.exec(content);
|
|
334
|
+
if (match) {
|
|
335
|
+
content = content.substring(match[0].length);
|
|
336
|
+
}
|
|
337
|
+
return content;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function termToSparql(term: Term): string {
|
|
341
|
+
if (term.termType === 'NamedNode') {
|
|
342
|
+
return `<${term.value}>`;
|
|
343
|
+
}
|
|
344
|
+
if (term.termType === 'BlankNode') {
|
|
345
|
+
return `_:${term.value}`;
|
|
346
|
+
}
|
|
347
|
+
if (term.termType === 'Literal') {
|
|
348
|
+
const escaped = term.value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
349
|
+
if (term.language) {
|
|
350
|
+
return `"${escaped}"@${term.language}`;
|
|
351
|
+
}
|
|
352
|
+
if (term.datatype?.value) {
|
|
353
|
+
return `"${escaped}"^^<${term.datatype.value}>`;
|
|
354
|
+
}
|
|
355
|
+
return `"${escaped}"`;
|
|
356
|
+
}
|
|
357
|
+
return `"${term.value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function normalizeSparqlVariableName(raw: string): string {
|
|
361
|
+
const normalized = raw.replace(/[^A-Za-z0-9_]/g, '_');
|
|
362
|
+
if (!normalized) {
|
|
363
|
+
return 'value';
|
|
364
|
+
}
|
|
365
|
+
if (!/^[A-Za-z_]/.test(normalized)) {
|
|
366
|
+
return `v_${normalized}`;
|
|
367
|
+
}
|
|
368
|
+
return normalized;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function uniqueVariableName(base: string, used: ReadonlySet<string>): string {
|
|
372
|
+
if (!used.has(base)) {
|
|
373
|
+
return base;
|
|
374
|
+
}
|
|
375
|
+
let index = 2;
|
|
376
|
+
while (used.has(`${base}_${index}`)) {
|
|
377
|
+
index += 1;
|
|
378
|
+
}
|
|
379
|
+
return `${base}_${index}`;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function localName(iri: string): string {
|
|
383
|
+
const hash = iri.lastIndexOf('#');
|
|
384
|
+
if (hash >= 0 && hash < iri.length - 1) {
|
|
385
|
+
return iri.slice(hash + 1);
|
|
386
|
+
}
|
|
387
|
+
const slash = iri.lastIndexOf('/');
|
|
388
|
+
if (slash >= 0 && slash < iri.length - 1) {
|
|
389
|
+
return iri.slice(slash + 1);
|
|
390
|
+
}
|
|
391
|
+
return iri;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function isRdfTerm(value: unknown): value is Term {
|
|
395
|
+
return typeof value === 'object' && value !== null && 'termType' in value;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function escapeIriForSparql(iri: string): string {
|
|
399
|
+
return iri.replace(/>/g, '\\>');
|
|
400
|
+
}
|