@shaclmate/compiler 2.0.12
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/ShapesGraphToAstTransformer.d.ts +32 -0
- package/ShapesGraphToAstTransformer.js +461 -0
- package/ast/Ast.d.ts +5 -0
- package/ast/Ast.js +2 -0
- package/ast/IdentifierType.d.ts +13 -0
- package/ast/IdentifierType.js +2 -0
- package/ast/IntersectionType.d.ts +9 -0
- package/ast/IntersectionType.js +2 -0
- package/ast/LiteralType.d.ts +13 -0
- package/ast/LiteralType.js +2 -0
- package/ast/MintingStrategy.d.ts +8 -0
- package/ast/MintingStrategy.js +9 -0
- package/ast/Name.d.ts +13 -0
- package/ast/Name.js +2 -0
- package/ast/ObjectType.d.ts +88 -0
- package/ast/ObjectType.js +2 -0
- package/ast/OptionType.d.ts +11 -0
- package/ast/OptionType.js +2 -0
- package/ast/SetType.d.ts +12 -0
- package/ast/SetType.js +2 -0
- package/ast/Type.d.ts +9 -0
- package/ast/Type.js +2 -0
- package/ast/UnionType.d.ts +9 -0
- package/ast/UnionType.js +2 -0
- package/ast/index.d.ts +10 -0
- package/ast/index.js +10 -0
- package/generators/index.d.ts +3 -0
- package/generators/index.js +3 -0
- package/generators/json/AstJsonGenerator.d.ts +7 -0
- package/generators/json/AstJsonGenerator.js +103 -0
- package/generators/json/index.d.ts +2 -0
- package/generators/json/index.js +2 -0
- package/generators/ts/BooleanType.d.ts +7 -0
- package/generators/ts/BooleanType.js +13 -0
- package/generators/ts/Configuration.d.ts +29 -0
- package/generators/ts/Configuration.js +40 -0
- package/generators/ts/IdentifierType.d.ts +23 -0
- package/generators/ts/IdentifierType.js +83 -0
- package/generators/ts/IntersectionType.d.ts +14 -0
- package/generators/ts/IntersectionType.js +26 -0
- package/generators/ts/ListType.d.ts +26 -0
- package/generators/ts/ListType.js +119 -0
- package/generators/ts/LiteralType.d.ts +14 -0
- package/generators/ts/LiteralType.js +61 -0
- package/generators/ts/NumberType.d.ts +7 -0
- package/generators/ts/NumberType.js +13 -0
- package/generators/ts/ObjectType.d.ts +71 -0
- package/generators/ts/ObjectType.js +160 -0
- package/generators/ts/OptionType.d.ts +17 -0
- package/generators/ts/OptionType.js +83 -0
- package/generators/ts/PrimitiveType.d.ts +11 -0
- package/generators/ts/PrimitiveType.js +41 -0
- package/generators/ts/RdfjsTermType.d.ts +25 -0
- package/generators/ts/RdfjsTermType.js +46 -0
- package/generators/ts/SetType.d.ts +19 -0
- package/generators/ts/SetType.js +76 -0
- package/generators/ts/StringType.d.ts +7 -0
- package/generators/ts/StringType.js +13 -0
- package/generators/ts/TsGenerator.d.ts +15 -0
- package/generators/ts/TsGenerator.js +119 -0
- package/generators/ts/Type.d.ts +108 -0
- package/generators/ts/Type.js +83 -0
- package/generators/ts/TypeFactory.d.ts +15 -0
- package/generators/ts/TypeFactory.js +198 -0
- package/generators/ts/UnionType.d.ts +18 -0
- package/generators/ts/UnionType.js +165 -0
- package/generators/ts/_ObjectType/IdentifierProperty.d.ts +23 -0
- package/generators/ts/_ObjectType/IdentifierProperty.js +82 -0
- package/generators/ts/_ObjectType/Property.d.ts +38 -0
- package/generators/ts/_ObjectType/Property.js +11 -0
- package/generators/ts/_ObjectType/ShaclProperty.d.ts +25 -0
- package/generators/ts/_ObjectType/ShaclProperty.js +106 -0
- package/generators/ts/_ObjectType/TypeDiscriminatorProperty.d.ts +29 -0
- package/generators/ts/_ObjectType/TypeDiscriminatorProperty.js +53 -0
- package/generators/ts/_ObjectType/classDeclaration.d.ts +4 -0
- package/generators/ts/_ObjectType/classDeclaration.js +154 -0
- package/generators/ts/_ObjectType/equalsFunctionDeclaration.d.ts +4 -0
- package/generators/ts/_ObjectType/equalsFunctionDeclaration.js +27 -0
- package/generators/ts/_ObjectType/fromRdfFunctionDeclaration.d.ts +4 -0
- package/generators/ts/_ObjectType/fromRdfFunctionDeclaration.js +72 -0
- package/generators/ts/_ObjectType/hashFunctionDeclaration.d.ts +5 -0
- package/generators/ts/_ObjectType/hashFunctionDeclaration.js +64 -0
- package/generators/ts/_ObjectType/index.d.ts +12 -0
- package/generators/ts/_ObjectType/index.js +12 -0
- package/generators/ts/_ObjectType/interfaceDeclaration.d.ts +4 -0
- package/generators/ts/_ObjectType/interfaceDeclaration.js +12 -0
- package/generators/ts/_ObjectType/sparqlGraphPatternsClassDeclaration.d.ts +4 -0
- package/generators/ts/_ObjectType/sparqlGraphPatternsClassDeclaration.js +46 -0
- package/generators/ts/_ObjectType/toRdfFunctionDeclaration.d.ts +4 -0
- package/generators/ts/_ObjectType/toRdfFunctionDeclaration.js +53 -0
- package/generators/ts/index.d.ts +2 -0
- package/generators/ts/index.js +2 -0
- package/generators/ts/rdfjsTermExpression.d.ts +5 -0
- package/generators/ts/rdfjsTermExpression.js +15 -0
- package/generators/ts/stringToValidTsIdentifier.d.ts +2 -0
- package/generators/ts/stringToValidTsIdentifier.js +14 -0
- package/generators/ts/tsName.d.ts +3 -0
- package/generators/ts/tsName.js +22 -0
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/logger.d.ts +2 -0
- package/logger.js +8 -0
- package/package.json +71 -0
- package/vocabularies/dashDataset.d.ts +3 -0
- package/vocabularies/dashDataset.js +2370 -0
- package/vocabularies/index.d.ts +2 -0
- package/vocabularies/index.js +2 -0
- package/vocabularies/shaclmate.d.ts +15 -0
- package/vocabularies/shaclmate.js +6 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type PrefixMap from "@rdfjs/prefix-map/PrefixMap.js";
|
|
2
|
+
import { Either } from "purify-ts";
|
|
3
|
+
import * as shaclAst from "shacl-ast";
|
|
4
|
+
import type * as ast from "./ast";
|
|
5
|
+
export declare class ShapesGraphToAstTransformer {
|
|
6
|
+
private readonly astObjectTypePropertiesByIdentifier;
|
|
7
|
+
private readonly iriPrefixMap;
|
|
8
|
+
private readonly nodeShapeAstTypesByIdentifier;
|
|
9
|
+
private readonly shapesGraph;
|
|
10
|
+
constructor({ iriPrefixMap, shapesGraph, }: {
|
|
11
|
+
iriPrefixMap: PrefixMap;
|
|
12
|
+
shapesGraph: shaclAst.ShapesGraph;
|
|
13
|
+
});
|
|
14
|
+
transform(): Either<Error, ast.Ast>;
|
|
15
|
+
/**
|
|
16
|
+
* Is an ast.ObjectType actually the shape of an RDF list?
|
|
17
|
+
* If so, return the type of its rdf:first.
|
|
18
|
+
*/
|
|
19
|
+
private astObjectTypeListItemType;
|
|
20
|
+
private nodeShapeAstType;
|
|
21
|
+
private propertyShapeAstObjectTypeProperty;
|
|
22
|
+
/**
|
|
23
|
+
* Try to convert a property shape to a type using some heuristics.
|
|
24
|
+
*
|
|
25
|
+
* We don't try to handle exotic cases allowed by the SHACL spec, such as combinations of sh:in and sh:node. Instead we assume
|
|
26
|
+
* a shape has one type.
|
|
27
|
+
*/
|
|
28
|
+
private propertyShapeAstType;
|
|
29
|
+
private resolveClassAstObjectType;
|
|
30
|
+
private shapeName;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=ShapesGraphToAstTransformer.d.ts.map
|
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
import TermMap from "@rdfjs/term-map";
|
|
2
|
+
import TermSet from "@rdfjs/term-set";
|
|
3
|
+
import { dash, owl, rdf, rdfs } from "@tpluscode/rdf-ns-builders";
|
|
4
|
+
import { Either, Left, Maybe } from "purify-ts";
|
|
5
|
+
import * as shaclAst from "shacl-ast";
|
|
6
|
+
import { invariant } from "ts-invariant";
|
|
7
|
+
import { MintingStrategy } from "./ast/MintingStrategy";
|
|
8
|
+
import { logger } from "./logger.js";
|
|
9
|
+
import { shaclmate } from "./vocabularies/";
|
|
10
|
+
function ancestorClassIris(classResource, maxDepth) {
|
|
11
|
+
const ancestorClassIris = new TermSet();
|
|
12
|
+
function ancestorClassIrisRecursive(classResource, depth) {
|
|
13
|
+
for (const parentClassValue of classResource.values(rdfs.subClassOf)) {
|
|
14
|
+
parentClassValue.toNamedResource().ifRight((parentClassResource) => {
|
|
15
|
+
if (ancestorClassIris.has(parentClassResource.identifier)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
ancestorClassIris.add(parentClassResource.identifier);
|
|
19
|
+
if (depth < maxDepth) {
|
|
20
|
+
ancestorClassIrisRecursive(parentClassResource, depth + 1);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
ancestorClassIrisRecursive(classResource, 1);
|
|
26
|
+
return [...ancestorClassIris];
|
|
27
|
+
}
|
|
28
|
+
function descendantClassIris(classResource, maxDepth) {
|
|
29
|
+
const descendantClassIris = new TermSet();
|
|
30
|
+
function descendantClassIrisRecursive(classResource, depth) {
|
|
31
|
+
for (const childClassValue of classResource.valuesOf(rdfs.subClassOf)) {
|
|
32
|
+
childClassValue.toNamedResource().ifRight((childClassResource) => {
|
|
33
|
+
if (descendantClassIris.has(childClassResource.identifier)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
descendantClassIris.add(childClassResource.identifier);
|
|
37
|
+
if (depth < maxDepth) {
|
|
38
|
+
descendantClassIrisRecursive(childClassResource, depth + 1);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
descendantClassIrisRecursive(classResource, 1);
|
|
44
|
+
return [...descendantClassIris];
|
|
45
|
+
}
|
|
46
|
+
function shaclmateInline(shape) {
|
|
47
|
+
return shape.resource
|
|
48
|
+
.value(shaclmate.inline)
|
|
49
|
+
.chain((value) => value.toBoolean())
|
|
50
|
+
.toMaybe();
|
|
51
|
+
}
|
|
52
|
+
function shaclmateName(shape) {
|
|
53
|
+
return shape.resource
|
|
54
|
+
.value(shaclmate.name)
|
|
55
|
+
.chain((value) => value.toString())
|
|
56
|
+
.toMaybe();
|
|
57
|
+
}
|
|
58
|
+
export class ShapesGraphToAstTransformer {
|
|
59
|
+
constructor({ iriPrefixMap, shapesGraph, }) {
|
|
60
|
+
this.astObjectTypePropertiesByIdentifier = new TermMap();
|
|
61
|
+
this.nodeShapeAstTypesByIdentifier = new TermMap();
|
|
62
|
+
this.iriPrefixMap = iriPrefixMap;
|
|
63
|
+
this.shapesGraph = shapesGraph;
|
|
64
|
+
}
|
|
65
|
+
transform() {
|
|
66
|
+
return Either.sequence(this.shapesGraph.nodeShapes
|
|
67
|
+
.filter((nodeShape) => nodeShape.resource.identifier.termType === "NamedNode" &&
|
|
68
|
+
!nodeShape.resource.identifier.value.startsWith(dash[""].value))
|
|
69
|
+
.map((nodeShape) => this.nodeShapeAstType(nodeShape))).map((nodeShapeTypes) => ({
|
|
70
|
+
objectTypes: nodeShapeTypes.filter((nodeShapeType) => nodeShapeType.kind === "ObjectType"),
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Is an ast.ObjectType actually the shape of an RDF list?
|
|
75
|
+
* If so, return the type of its rdf:first.
|
|
76
|
+
*/
|
|
77
|
+
astObjectTypeListItemType(astObjectType, nodeShape) {
|
|
78
|
+
if (!nodeShape.resource.isSubClassOf(rdf.List)) {
|
|
79
|
+
return Left(new Error(`${nodeShape} is not an rdfs:subClassOf rdf:List`));
|
|
80
|
+
}
|
|
81
|
+
if (astObjectType.properties.length !== 2) {
|
|
82
|
+
return Left(new Error(`${nodeShape} does not have exactly two properties`));
|
|
83
|
+
}
|
|
84
|
+
// rdf:first can have any type
|
|
85
|
+
// The type of the rdf:first property is the list item type.
|
|
86
|
+
const firstProperty = astObjectType.properties.find((property) => property.path.iri.equals(rdf.first));
|
|
87
|
+
if (!firstProperty) {
|
|
88
|
+
return Left(new Error(`${nodeShape} does not have an rdf:first property`));
|
|
89
|
+
}
|
|
90
|
+
const restProperty = astObjectType.properties.find((property) => property.path.iri.equals(rdf.rest));
|
|
91
|
+
if (!restProperty) {
|
|
92
|
+
return Left(new Error(`${nodeShape} does not have an rdf:rest property`));
|
|
93
|
+
}
|
|
94
|
+
if (restProperty.type.kind !== "UnionType") {
|
|
95
|
+
return Left(new Error(`${nodeShape} rdf:rest property is not sh:or`));
|
|
96
|
+
}
|
|
97
|
+
if (restProperty.type.memberTypes.length !== 2) {
|
|
98
|
+
return Left(new Error(`${nodeShape} rdf:rest property sh:or does not have exactly two member types`));
|
|
99
|
+
}
|
|
100
|
+
// rdf:rest should be sh:or ( [ sh:class nodeShape ] [ sh:hasValue rdf:nil ] )
|
|
101
|
+
if (!restProperty.type.memberTypes.find((type) => type.kind === "ObjectType" &&
|
|
102
|
+
type.name.identifier.equals(nodeShape.resource.identifier))) {
|
|
103
|
+
return Left(new Error(`${nodeShape} rdf:rest property sh:or is not recursive into the node shape`));
|
|
104
|
+
}
|
|
105
|
+
if (!restProperty.type.memberTypes.find((type) => type.kind === "IdentifierType")) {
|
|
106
|
+
return Left(new Error(`${nodeShape} rdf:rest property sh:or does not include sh:hasValue rdf:nil`));
|
|
107
|
+
}
|
|
108
|
+
return Either.of(firstProperty.type);
|
|
109
|
+
}
|
|
110
|
+
nodeShapeAstType(nodeShape) {
|
|
111
|
+
{
|
|
112
|
+
const type = this.nodeShapeAstTypesByIdentifier.get(nodeShape.resource.identifier);
|
|
113
|
+
if (type) {
|
|
114
|
+
return Either.of(type);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// https://www.w3.org/TR/shacl/#implicit-targetClass
|
|
118
|
+
// If the node shape is an owl:class or rdfs:Class, make the ObjectType have an rdf:type of the NodeShape.
|
|
119
|
+
const rdfType = nodeShape.resource.isInstanceOf(owl.Class) ||
|
|
120
|
+
nodeShape.resource.isInstanceOf(rdfs.Class)
|
|
121
|
+
? Maybe.of(nodeShape.resource.identifier)
|
|
122
|
+
: Maybe.empty();
|
|
123
|
+
const nodeKinds = new Set([...nodeShape.constraints.nodeKinds].filter((nodeKind) => nodeKind !== shaclAst.NodeKind.LITERAL));
|
|
124
|
+
if (nodeKinds.size === 0) {
|
|
125
|
+
return Left(new Error(`${nodeShape} has no non-Literal node kinds`));
|
|
126
|
+
}
|
|
127
|
+
// Put a placeholder in the cache to deal with cyclic references
|
|
128
|
+
// If this node shape's properties (directly or indirectly) refer to the node shape itself,
|
|
129
|
+
// we'll return this placeholder.
|
|
130
|
+
const objectType = {
|
|
131
|
+
abstract: nodeShape.resource
|
|
132
|
+
.value(shaclmate.abstract)
|
|
133
|
+
.chain((value) => value.toBoolean())
|
|
134
|
+
.orDefault(false),
|
|
135
|
+
ancestorObjectTypes: [],
|
|
136
|
+
childObjectTypes: [],
|
|
137
|
+
descendantObjectTypes: [],
|
|
138
|
+
export: nodeShape.resource
|
|
139
|
+
.value(shaclmate.export)
|
|
140
|
+
.chain((value) => value.toBoolean())
|
|
141
|
+
.orDefault(true),
|
|
142
|
+
kind: "ObjectType",
|
|
143
|
+
listItemType: Maybe.empty(),
|
|
144
|
+
mintingStrategy: nodeShape.resource
|
|
145
|
+
.value(shaclmate.mintingStrategy)
|
|
146
|
+
.chain((value) => value.toIri())
|
|
147
|
+
.chain((iri) => {
|
|
148
|
+
if (iri.equals(shaclmate.SHA256)) {
|
|
149
|
+
return Either.of(MintingStrategy.SHA256);
|
|
150
|
+
}
|
|
151
|
+
if (iri.equals(shaclmate.UUIDv4)) {
|
|
152
|
+
return Either.of(MintingStrategy.UUIDv4);
|
|
153
|
+
}
|
|
154
|
+
return Left(new Error(`unrecognizing minting strategy: ${iri.value}`));
|
|
155
|
+
})
|
|
156
|
+
.toMaybe(),
|
|
157
|
+
name: this.shapeName(nodeShape),
|
|
158
|
+
nodeKinds,
|
|
159
|
+
properties: [], // This is mutable, we'll populate it below.
|
|
160
|
+
rdfType,
|
|
161
|
+
parentObjectTypes: [], // This is mutable, we'll populate it below
|
|
162
|
+
};
|
|
163
|
+
this.nodeShapeAstTypesByIdentifier.set(nodeShape.resource.identifier, objectType);
|
|
164
|
+
// Populate ancestor and descendant object types
|
|
165
|
+
// Ancestors
|
|
166
|
+
for (const classIri of ancestorClassIris(nodeShape.resource, Number.MAX_SAFE_INTEGER)) {
|
|
167
|
+
this.resolveClassAstObjectType(classIri).ifRight((ancestorObjectType) => objectType.ancestorObjectTypes.push(ancestorObjectType));
|
|
168
|
+
}
|
|
169
|
+
// Parents
|
|
170
|
+
for (const classIri of ancestorClassIris(nodeShape.resource, 1)) {
|
|
171
|
+
this.resolveClassAstObjectType(classIri).ifRight((parentObjectType) => objectType.parentObjectTypes.push(parentObjectType));
|
|
172
|
+
}
|
|
173
|
+
// Descendants
|
|
174
|
+
for (const classIri of descendantClassIris(nodeShape.resource, Number.MAX_SAFE_INTEGER)) {
|
|
175
|
+
this.resolveClassAstObjectType(classIri).ifRight((descendantObjectType) => objectType.descendantObjectTypes.push(descendantObjectType));
|
|
176
|
+
}
|
|
177
|
+
// Children
|
|
178
|
+
for (const classIri of descendantClassIris(nodeShape.resource, 1)) {
|
|
179
|
+
this.resolveClassAstObjectType(classIri).ifRight((childObjectType) => objectType.childObjectTypes.push(childObjectType));
|
|
180
|
+
}
|
|
181
|
+
// Populate properties
|
|
182
|
+
for (const propertyShape of nodeShape.constraints.properties) {
|
|
183
|
+
const propertyEither = this.propertyShapeAstObjectTypeProperty(propertyShape);
|
|
184
|
+
if (propertyEither.isLeft()) {
|
|
185
|
+
logger.warn("error transforming %s %s: %s", nodeShape, propertyShape, propertyEither.extract().message);
|
|
186
|
+
continue;
|
|
187
|
+
// return property;
|
|
188
|
+
}
|
|
189
|
+
objectType.properties.push(propertyEither.unsafeCoerce());
|
|
190
|
+
}
|
|
191
|
+
// Is the object type an RDF list?
|
|
192
|
+
objectType.listItemType = this.astObjectTypeListItemType(objectType, nodeShape).toMaybe();
|
|
193
|
+
return Either.of(objectType);
|
|
194
|
+
}
|
|
195
|
+
propertyShapeAstObjectTypeProperty(propertyShape) {
|
|
196
|
+
{
|
|
197
|
+
const property = this.astObjectTypePropertiesByIdentifier.get(propertyShape.resource.identifier);
|
|
198
|
+
if (property) {
|
|
199
|
+
return Either.of(property);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const type = this.propertyShapeAstType(propertyShape, null);
|
|
203
|
+
if (type.isLeft()) {
|
|
204
|
+
return type;
|
|
205
|
+
}
|
|
206
|
+
const path = propertyShape.path;
|
|
207
|
+
if (path.kind !== "PredicatePath") {
|
|
208
|
+
return Left(new Error(`${propertyShape} has non-predicate path, unsupported`));
|
|
209
|
+
}
|
|
210
|
+
const property = {
|
|
211
|
+
inline: shaclmateInline(propertyShape).orDefault(false),
|
|
212
|
+
name: this.shapeName(propertyShape),
|
|
213
|
+
path,
|
|
214
|
+
type: type.extract(),
|
|
215
|
+
};
|
|
216
|
+
this.astObjectTypePropertiesByIdentifier.set(propertyShape.resource.identifier, property);
|
|
217
|
+
return Either.of(property);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Try to convert a property shape to a type using some heuristics.
|
|
221
|
+
*
|
|
222
|
+
* We don't try to handle exotic cases allowed by the SHACL spec, such as combinations of sh:in and sh:node. Instead we assume
|
|
223
|
+
* a shape has one type.
|
|
224
|
+
*/
|
|
225
|
+
propertyShapeAstType(shape, inherited) {
|
|
226
|
+
const defaultValue = (shape instanceof shaclAst.PropertyShape
|
|
227
|
+
? shape.defaultValue
|
|
228
|
+
: Maybe.empty()).alt(inherited !== null ? inherited.defaultValue : Maybe.empty());
|
|
229
|
+
const hasValue = shape.constraints.hasValue;
|
|
230
|
+
const inline = shaclmateInline(shape).alt(inherited !== null ? inherited.inline : Maybe.empty());
|
|
231
|
+
return (() => {
|
|
232
|
+
// Conjunctions/disjunctions of multiple types
|
|
233
|
+
if (shape.constraints.and.length > 0 ||
|
|
234
|
+
shape.constraints.classes.length > 0 ||
|
|
235
|
+
shape.constraints.nodes.length > 0 ||
|
|
236
|
+
shape.constraints.or.length > 0) {
|
|
237
|
+
let memberTypeEithers;
|
|
238
|
+
let compositeTypeKind;
|
|
239
|
+
if (shape.constraints.and.length > 0) {
|
|
240
|
+
memberTypeEithers = shape.constraints.and.map((memberShape) => this.propertyShapeAstType(memberShape, {
|
|
241
|
+
defaultValue,
|
|
242
|
+
inline,
|
|
243
|
+
}));
|
|
244
|
+
compositeTypeKind = "IntersectionType";
|
|
245
|
+
}
|
|
246
|
+
else if (shape.constraints.classes.length > 0) {
|
|
247
|
+
memberTypeEithers = shape.constraints.classes.map((classIri) => this.resolveClassAstObjectType(classIri).map((classObjectType) => inline.orDefault(false)
|
|
248
|
+
? classObjectType
|
|
249
|
+
: {
|
|
250
|
+
defaultValue: defaultValue.filter((term) => term.termType !== "Literal"),
|
|
251
|
+
hasValue: Maybe.empty(),
|
|
252
|
+
kind: "IdentifierType",
|
|
253
|
+
nodeKinds: classObjectType.nodeKinds,
|
|
254
|
+
}));
|
|
255
|
+
compositeTypeKind = "IntersectionType";
|
|
256
|
+
}
|
|
257
|
+
else if (shape.constraints.nodes.length > 0) {
|
|
258
|
+
memberTypeEithers = shape.constraints.nodes.map((nodeShape) => this.nodeShapeAstType(nodeShape));
|
|
259
|
+
compositeTypeKind = "IntersectionType";
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
memberTypeEithers = shape.constraints.or.map((memberShape) => this.propertyShapeAstType(memberShape, {
|
|
263
|
+
defaultValue,
|
|
264
|
+
inline,
|
|
265
|
+
}));
|
|
266
|
+
compositeTypeKind = "UnionType";
|
|
267
|
+
}
|
|
268
|
+
invariant(memberTypeEithers.length > 0);
|
|
269
|
+
const memberTypes = Either.rights(memberTypeEithers);
|
|
270
|
+
if (memberTypes.length !== memberTypeEithers.length) {
|
|
271
|
+
logger.warn("shape %s composition did not map all member types successfully", shape);
|
|
272
|
+
return memberTypeEithers[0];
|
|
273
|
+
}
|
|
274
|
+
invariant(memberTypes.length > 0);
|
|
275
|
+
if (memberTypes.length === 1) {
|
|
276
|
+
return Either.of(memberTypes[0]);
|
|
277
|
+
}
|
|
278
|
+
// Get the type underlying a set or option
|
|
279
|
+
const memberItemTypes = memberTypes.map((memberType) => {
|
|
280
|
+
switch (memberType.kind) {
|
|
281
|
+
case "SetType":
|
|
282
|
+
return memberType.itemType;
|
|
283
|
+
case "OptionType":
|
|
284
|
+
return memberType.itemType;
|
|
285
|
+
default:
|
|
286
|
+
return memberType;
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
if (hasValue.isNothing() &&
|
|
290
|
+
memberItemTypes.every((memberItemType) => memberItemType.kind === "LiteralType" &&
|
|
291
|
+
memberItemType.maxExclusive.isNothing() &&
|
|
292
|
+
memberItemType.maxInclusive.isNothing() &&
|
|
293
|
+
memberItemType.minExclusive.isNothing() &&
|
|
294
|
+
memberItemType.minInclusive.isNothing())) {
|
|
295
|
+
// Special case: all the member types are Literals without further constraints,
|
|
296
|
+
// like dash:StringOrLangString
|
|
297
|
+
return Either.of({
|
|
298
|
+
datatype: Maybe.empty(),
|
|
299
|
+
defaultValue: defaultValue.filter((term) => term.termType === "Literal"),
|
|
300
|
+
hasValue: Maybe.empty(),
|
|
301
|
+
kind: "LiteralType",
|
|
302
|
+
maxExclusive: Maybe.empty(),
|
|
303
|
+
maxInclusive: Maybe.empty(),
|
|
304
|
+
minExclusive: Maybe.empty(),
|
|
305
|
+
minInclusive: Maybe.empty(),
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
if (hasValue.isNothing() &&
|
|
309
|
+
memberItemTypes.every((memberItemType) => memberItemType.kind === "IdentifierType")) {
|
|
310
|
+
// Special case: all member types are blank or named nodes without further constraints
|
|
311
|
+
return Either.of({
|
|
312
|
+
defaultValue: defaultValue.filter((term) => term.termType !== "Literal"),
|
|
313
|
+
hasValue: Maybe.empty(),
|
|
314
|
+
kind: "IdentifierType",
|
|
315
|
+
nodeKinds: new Set(memberItemTypes
|
|
316
|
+
.filter((memberItemType) => memberItemType.kind === "IdentifierType")
|
|
317
|
+
.flatMap((memberItemType) => [...memberItemType.nodeKinds])),
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
return Either.of({
|
|
321
|
+
kind: compositeTypeKind,
|
|
322
|
+
memberTypes: memberTypes,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
if (
|
|
326
|
+
// Treat any shape with the constraints in the list as a literal type
|
|
327
|
+
[
|
|
328
|
+
shape.constraints.datatype,
|
|
329
|
+
shape.constraints.maxExclusive,
|
|
330
|
+
shape.constraints.maxInclusive,
|
|
331
|
+
shape.constraints.minExclusive,
|
|
332
|
+
shape.constraints.minInclusive,
|
|
333
|
+
].some((constraint) => constraint.isJust()) ||
|
|
334
|
+
// Treat any shape with a literal value as a literal type
|
|
335
|
+
hasValue.extractNullable()?.termType === "Literal" ||
|
|
336
|
+
// Treat any shape with a single sh:nodeKind of sh:Literal as a literal type
|
|
337
|
+
(shape.constraints.nodeKinds.size === 1 &&
|
|
338
|
+
shape.constraints.nodeKinds.has(shaclAst.NodeKind.LITERAL))) {
|
|
339
|
+
return Either.of({
|
|
340
|
+
datatype: shape.constraints.datatype,
|
|
341
|
+
defaultValue: defaultValue.filter((term) => term.termType === "Literal"),
|
|
342
|
+
hasValue: hasValue.filter((term) => term.termType === "Literal"),
|
|
343
|
+
kind: "LiteralType",
|
|
344
|
+
maxExclusive: shape.constraints.maxExclusive,
|
|
345
|
+
maxInclusive: shape.constraints.maxInclusive,
|
|
346
|
+
minExclusive: shape.constraints.minExclusive,
|
|
347
|
+
minInclusive: shape.constraints.minInclusive,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
// Treat any shape with sh:nodeKind blank node or IRI as an identifier type
|
|
351
|
+
const identifierDefaultValue = defaultValue.filter((value) => value.termType !== "Literal");
|
|
352
|
+
const hasIdentifierValue = hasValue.filter((value) => value.termType !== "Literal");
|
|
353
|
+
if (hasIdentifierValue.isJust() ||
|
|
354
|
+
identifierDefaultValue.isJust() ||
|
|
355
|
+
(shape.constraints.nodeKinds.size > 0 &&
|
|
356
|
+
shape.constraints.nodeKinds.size <= 2 &&
|
|
357
|
+
!shape.constraints.nodeKinds.has(shaclAst.NodeKind.LITERAL))) {
|
|
358
|
+
const nodeKinds = hasIdentifierValue
|
|
359
|
+
.map((value) => {
|
|
360
|
+
const nodeKinds = new Set();
|
|
361
|
+
switch (value.termType) {
|
|
362
|
+
case "BlankNode":
|
|
363
|
+
nodeKinds.add(shaclAst.NodeKind.BLANK_NODE);
|
|
364
|
+
break;
|
|
365
|
+
case "NamedNode":
|
|
366
|
+
nodeKinds.add(shaclAst.NodeKind.IRI);
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
return nodeKinds;
|
|
370
|
+
})
|
|
371
|
+
.orDefaultLazy(() => shape.constraints.nodeKinds);
|
|
372
|
+
invariant(nodeKinds.size > 0);
|
|
373
|
+
return Either.of({
|
|
374
|
+
defaultValue: identifierDefaultValue,
|
|
375
|
+
hasValue: hasIdentifierValue,
|
|
376
|
+
kind: "IdentifierType",
|
|
377
|
+
nodeKinds,
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
return Left(new Error(`unable to transform type on ${shape}`));
|
|
381
|
+
})().map((itemType) => {
|
|
382
|
+
// Handle cardinality constraints
|
|
383
|
+
if (defaultValue.isJust()) {
|
|
384
|
+
// Ignore other cardinality constraints if there's a default value and treat the type as minCount=maxCount=1
|
|
385
|
+
return itemType;
|
|
386
|
+
}
|
|
387
|
+
const maxCount = shape.constraints.maxCount;
|
|
388
|
+
const minCount = shape.constraints.minCount;
|
|
389
|
+
if (maxCount.isNothing() && minCount.isNothing()) {
|
|
390
|
+
// The shape has no cardinality constraints
|
|
391
|
+
if (inherited === null) {
|
|
392
|
+
// The shape is top-level (not an sh:or/sh:and of a top-level shape)
|
|
393
|
+
// Treat it as a Set, the default in RDF.
|
|
394
|
+
// We want Set to be the outermost type unless it's explicitly requested with sh:minCount 0.
|
|
395
|
+
return {
|
|
396
|
+
itemType,
|
|
397
|
+
kind: "SetType",
|
|
398
|
+
minCount: 0,
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
// else the shape is not top-level
|
|
402
|
+
// We want Set to be the outermost type, so just return the itemType here
|
|
403
|
+
return itemType;
|
|
404
|
+
}
|
|
405
|
+
if (minCount.orDefault(0) === 0 && maxCount.extractNullable() === 1) {
|
|
406
|
+
return {
|
|
407
|
+
itemType,
|
|
408
|
+
kind: "OptionType",
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
if (minCount.orDefault(0) === 1 && maxCount.extractNullable() === 1) {
|
|
412
|
+
return itemType;
|
|
413
|
+
}
|
|
414
|
+
invariant(minCount.isJust() || maxCount.isJust());
|
|
415
|
+
// There are cardinality constraints for a Set. It may be an inner type.
|
|
416
|
+
return {
|
|
417
|
+
itemType,
|
|
418
|
+
kind: "SetType",
|
|
419
|
+
minCount: minCount.orDefault(0),
|
|
420
|
+
};
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
resolveClassAstObjectType(classIri) {
|
|
424
|
+
let nodeShape;
|
|
425
|
+
if (classIri.equals(owl.Class) ||
|
|
426
|
+
classIri.equals(owl.Thing) ||
|
|
427
|
+
classIri.equals(rdfs.Class)) {
|
|
428
|
+
nodeShape = Maybe.empty();
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
nodeShape = this.shapesGraph.nodeShapeByNode(classIri);
|
|
432
|
+
}
|
|
433
|
+
return nodeShape
|
|
434
|
+
.toEither(new Error(`${classIri.value} does not correspond to a node shape`))
|
|
435
|
+
.chain((nodeShape) => this.nodeShapeAstType(nodeShape))
|
|
436
|
+
.ifLeft((error) => {
|
|
437
|
+
logger.debug("class %s did not resolve to an object type: %s", classIri.value, error.message);
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
shapeName(shape) {
|
|
441
|
+
let propertyPath = Maybe.empty();
|
|
442
|
+
if (shape instanceof shaclAst.PropertyShape &&
|
|
443
|
+
shape.path.kind === "PredicatePath") {
|
|
444
|
+
const pathIri = shape.path.iri;
|
|
445
|
+
propertyPath = Maybe.of({
|
|
446
|
+
curie: Maybe.fromNullable(this.iriPrefixMap.shrink(pathIri)?.value),
|
|
447
|
+
identifier: pathIri,
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
return {
|
|
451
|
+
curie: shape.resource.identifier.termType === "NamedNode"
|
|
452
|
+
? Maybe.fromNullable(this.iriPrefixMap.shrink(shape.resource.identifier)?.value)
|
|
453
|
+
: Maybe.empty(),
|
|
454
|
+
identifier: shape.resource.identifier,
|
|
455
|
+
propertyPath,
|
|
456
|
+
shName: shape.name.map((name) => name.value),
|
|
457
|
+
shaclmateName: shaclmateName(shape),
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
//# sourceMappingURL=ShapesGraphToAstTransformer.js.map
|
package/ast/Ast.d.ts
ADDED
package/ast/Ast.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { BlankNode, NamedNode } from "@rdfjs/types";
|
|
2
|
+
import type { Maybe } from "purify-ts";
|
|
3
|
+
import type { NodeKind } from "shacl-ast";
|
|
4
|
+
/**
|
|
5
|
+
* A type corresponding to sh:nodeKind of a blank node or IRI, and not corresponding to a node shape.
|
|
6
|
+
*/
|
|
7
|
+
export interface IdentifierType {
|
|
8
|
+
readonly defaultValue: Maybe<BlankNode | NamedNode>;
|
|
9
|
+
readonly hasValue: Maybe<BlankNode | NamedNode>;
|
|
10
|
+
readonly kind: "IdentifierType";
|
|
11
|
+
readonly nodeKinds: Set<NodeKind.BLANK_NODE | NodeKind.IRI>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=IdentifierType.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Type } from "./Type.js";
|
|
2
|
+
/**
|
|
3
|
+
* A conjunction ("and") of types, corresponding to an sh:and.
|
|
4
|
+
*/
|
|
5
|
+
export interface IntersectionType {
|
|
6
|
+
readonly kind: "IntersectionType";
|
|
7
|
+
readonly memberTypes: readonly Type[];
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=IntersectionType.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Literal, NamedNode } from "@rdfjs/types";
|
|
2
|
+
import type { Maybe } from "purify-ts";
|
|
3
|
+
export interface LiteralType {
|
|
4
|
+
readonly datatype: Maybe<NamedNode>;
|
|
5
|
+
readonly defaultValue: Maybe<Literal>;
|
|
6
|
+
readonly hasValue: Maybe<Literal>;
|
|
7
|
+
readonly kind: "LiteralType";
|
|
8
|
+
readonly maxExclusive: Maybe<Literal>;
|
|
9
|
+
readonly maxInclusive: Maybe<Literal>;
|
|
10
|
+
readonly minExclusive: Maybe<Literal>;
|
|
11
|
+
readonly minInclusive: Maybe<Literal>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=LiteralType.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript enum corresponding to shaclmate:mintingStrategy, for simpler manipulation.
|
|
3
|
+
*/
|
|
4
|
+
export var MintingStrategy;
|
|
5
|
+
(function (MintingStrategy) {
|
|
6
|
+
MintingStrategy[MintingStrategy["SHA256"] = 1] = "SHA256";
|
|
7
|
+
MintingStrategy[MintingStrategy["UUIDv4"] = 2] = "UUIDv4";
|
|
8
|
+
})(MintingStrategy || (MintingStrategy = {}));
|
|
9
|
+
//# sourceMappingURL=MintingStrategy.js.map
|
package/ast/Name.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { BlankNode, NamedNode } from "@rdfjs/types";
|
|
2
|
+
import type { Maybe } from "purify-ts";
|
|
3
|
+
export interface Name {
|
|
4
|
+
readonly curie: Maybe<string>;
|
|
5
|
+
readonly identifier: BlankNode | NamedNode;
|
|
6
|
+
readonly propertyPath: Maybe<{
|
|
7
|
+
curie: Maybe<string>;
|
|
8
|
+
identifier: NamedNode;
|
|
9
|
+
}>;
|
|
10
|
+
readonly shName: Maybe<string>;
|
|
11
|
+
readonly shaclmateName: Maybe<string>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=Name.d.ts.map
|
package/ast/Name.js
ADDED