@shaclmate/shacl-ast 2.0.13

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/NodeKind.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * TypeScript enum corresponding to sh:NodeKind, for simpler manipulation.
3
+ */
4
+ export declare enum NodeKind {
5
+ BLANK_NODE = 1,
6
+ IRI = 2,
7
+ LITERAL = 3
8
+ }
9
+ //# sourceMappingURL=NodeKind.d.ts.map
package/NodeKind.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * TypeScript enum corresponding to sh:NodeKind, for simpler manipulation.
3
+ */
4
+ export var NodeKind;
5
+ (function (NodeKind) {
6
+ NodeKind[NodeKind["BLANK_NODE"] = 1] = "BLANK_NODE";
7
+ NodeKind[NodeKind["IRI"] = 2] = "IRI";
8
+ NodeKind[NodeKind["LITERAL"] = 3] = "LITERAL";
9
+ })(NodeKind || (NodeKind = {}));
10
+ //# sourceMappingURL=NodeKind.js.map
package/NodeShape.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ import type { Maybe } from "purify-ts";
2
+ import type { Resource } from "rdfjs-resource";
3
+ import type { PropertyShape } from "./PropertyShape.js";
4
+ import { Shape } from "./Shape.js";
5
+ import type { ShapesGraph } from "./ShapesGraph.js";
6
+ export declare class NodeShape extends Shape {
7
+ readonly constraints: NodeShape.Constraints;
8
+ constructor(resource: Resource, shapesGraph: ShapesGraph);
9
+ toString(): string;
10
+ }
11
+ export declare namespace NodeShape {
12
+ class Constraints extends Shape.Constraints {
13
+ get closed(): Maybe<boolean>;
14
+ get properties(): readonly PropertyShape[];
15
+ }
16
+ }
17
+ //# sourceMappingURL=NodeShape.d.ts.map
package/NodeShape.js ADDED
@@ -0,0 +1,30 @@
1
+ import { sh } from "@tpluscode/rdf-ns-builders";
2
+ import { Shape } from "./Shape.js";
3
+ export class NodeShape extends Shape {
4
+ constructor(resource, shapesGraph) {
5
+ super(resource);
6
+ this.constraints = new NodeShape.Constraints(resource, shapesGraph);
7
+ }
8
+ toString() {
9
+ return `NodeShape(node=${this.resource.identifier.value})`;
10
+ }
11
+ }
12
+ (function (NodeShape) {
13
+ class Constraints extends Shape.Constraints {
14
+ get closed() {
15
+ return this.resource
16
+ .value(sh.closed)
17
+ .chain((value) => value.toBoolean())
18
+ .toMaybe();
19
+ }
20
+ get properties() {
21
+ return [...this.resource.values(sh.property)].flatMap((value) => value
22
+ .toIdentifier()
23
+ .toMaybe()
24
+ .chain((shapeNode) => this.shapesGraph.propertyShapeByNode(shapeNode))
25
+ .toList());
26
+ }
27
+ }
28
+ NodeShape.Constraints = Constraints;
29
+ })(NodeShape || (NodeShape = {}));
30
+ //# sourceMappingURL=NodeShape.js.map
@@ -0,0 +1,9 @@
1
+ import type { Literal } from "@rdfjs/types";
2
+ import type { Maybe } from "purify-ts";
3
+ import type { Resource } from "rdfjs-resource";
4
+ export declare class PropertyGroup {
5
+ private readonly resource;
6
+ constructor(resource: Resource);
7
+ get label(): Maybe<Literal>;
8
+ }
9
+ //# sourceMappingURL=PropertyGroup.d.ts.map
@@ -0,0 +1,13 @@
1
+ import { rdfs } from "@tpluscode/rdf-ns-builders";
2
+ export class PropertyGroup {
3
+ constructor(resource) {
4
+ this.resource = resource;
5
+ }
6
+ get label() {
7
+ return this.resource
8
+ .value(rdfs.label)
9
+ .chain((value) => value.toLiteral())
10
+ .toMaybe();
11
+ }
12
+ }
13
+ //# sourceMappingURL=PropertyGroup.js.map
@@ -0,0 +1,36 @@
1
+ import type { NamedNode } from "@rdfjs/types";
2
+ import { Either } from "purify-ts";
3
+ import { Resource } from "rdfjs-resource";
4
+ export interface AlternativePath {
5
+ readonly kind: "AlternativePath";
6
+ readonly members: readonly PropertyPath[];
7
+ }
8
+ export interface InversePath {
9
+ readonly kind: "InversePath";
10
+ readonly path: PropertyPath;
11
+ }
12
+ export interface OneOrMorePath {
13
+ readonly kind: "OneOrMorePath";
14
+ readonly path: PropertyPath;
15
+ }
16
+ export interface PredicatePath {
17
+ readonly iri: NamedNode;
18
+ readonly kind: "PredicatePath";
19
+ }
20
+ export interface SequencePath {
21
+ readonly kind: "SequencePath";
22
+ readonly members: readonly PropertyPath[];
23
+ }
24
+ export interface ZeroOrMorePath {
25
+ readonly kind: "ZeroOrMorePath";
26
+ readonly path: PropertyPath;
27
+ }
28
+ export interface ZeroOrOnePath {
29
+ readonly kind: "ZeroOrOnePath";
30
+ readonly path: PropertyPath;
31
+ }
32
+ export type PropertyPath = AlternativePath | InversePath | OneOrMorePath | PredicatePath | SequencePath | ZeroOrMorePath | ZeroOrOnePath;
33
+ export declare namespace PropertyPath {
34
+ function fromResource(resource: Resource): Either<Error, PropertyPath>;
35
+ }
36
+ //# sourceMappingURL=PropertyPath.d.ts.map
@@ -0,0 +1,91 @@
1
+ import { rdf, sh } from "@tpluscode/rdf-ns-builders";
2
+ import { Either, Left } from "purify-ts";
3
+ import { Resource } from "rdfjs-resource";
4
+ export var PropertyPath;
5
+ (function (PropertyPath) {
6
+ function fromResource(resource) {
7
+ // Predicate path
8
+ // sh:path ex:parent
9
+ if (resource.identifier.termType === "NamedNode") {
10
+ return Either.of({ iri: resource.identifier, kind: "PredicatePath" });
11
+ }
12
+ // The other property path types are BlankNodes
13
+ const getPropertyPathList = (listResource) => {
14
+ return listResource.toList().chain((values) => {
15
+ const members = [];
16
+ for (const value of values) {
17
+ const memberResource = value.toResource().toMaybe();
18
+ if (memberResource.isNothing()) {
19
+ return Left(new Error("non-identifier in property path list"));
20
+ }
21
+ const member = PropertyPath.fromResource(memberResource.unsafeCoerce());
22
+ if (member.isLeft()) {
23
+ return member;
24
+ }
25
+ members.push(member.unsafeCoerce());
26
+ }
27
+ return Either.of(members);
28
+ });
29
+ };
30
+ for (const quad of resource.dataset.match(resource.identifier, null, null, null)) {
31
+ switch (quad.object.termType) {
32
+ case "BlankNode":
33
+ case "NamedNode":
34
+ break;
35
+ default:
36
+ return Left(new Error(`non-BlankNode/NamedNode property path object on path ${resource.identifier.value}: ${quad.object.termType} ${quad.object.value}`));
37
+ }
38
+ const objectResource = new Resource({
39
+ dataset: resource.dataset,
40
+ identifier: quad.object,
41
+ });
42
+ // Alternative path
43
+ // sh:path: [ sh:alternativePath ( ex:father ex:mother ) ]
44
+ if (quad.predicate.equals(sh.alternativePath)) {
45
+ return getPropertyPathList(objectResource).map((members) => ({
46
+ kind: "AlternativePath",
47
+ members,
48
+ }));
49
+ }
50
+ // Inverse path
51
+ // sh:path: [ sh:inversePath ex:parent ]
52
+ if (quad.predicate.equals(sh.inversePath)) {
53
+ return PropertyPath.fromResource(objectResource).map((path) => ({
54
+ kind: "InversePath",
55
+ path,
56
+ }));
57
+ }
58
+ // One or more path
59
+ if (quad.predicate.equals(sh.oneOrMorePath)) {
60
+ return PropertyPath.fromResource(objectResource).map((path) => ({
61
+ kind: "OneOrMorePath",
62
+ path,
63
+ }));
64
+ }
65
+ // Sequence path
66
+ // sh:path ( ex:parent ex:firstName )
67
+ if (quad.predicate.equals(rdf.first)) {
68
+ return getPropertyPathList(objectResource).map((members) => ({
69
+ kind: "SequencePath",
70
+ members,
71
+ }));
72
+ }
73
+ // Zero or more path
74
+ if (quad.predicate.equals(sh.zeroOrMorePath)) {
75
+ return PropertyPath.fromResource(objectResource).map((path) => ({
76
+ kind: "ZeroOrMorePath",
77
+ path,
78
+ }));
79
+ }
80
+ if (quad.predicate.equals(sh.zeroOrOnePath)) {
81
+ return PropertyPath.fromResource(objectResource).map((path) => ({
82
+ kind: "ZeroOrOnePath",
83
+ path,
84
+ }));
85
+ }
86
+ }
87
+ return Left(new Error(`unrecognized or ill-formed SHACL property path ${resource.identifier.value}`));
88
+ }
89
+ PropertyPath.fromResource = fromResource;
90
+ })(PropertyPath || (PropertyPath = {}));
91
+ //# sourceMappingURL=PropertyPath.js.map
@@ -0,0 +1,21 @@
1
+ import type { BlankNode, Literal, NamedNode } from "@rdfjs/types";
2
+ import type { Maybe } from "purify-ts";
3
+ import type { Resource } from "rdfjs-resource";
4
+ import type { PropertyGroup } from "./PropertyGroup.js";
5
+ import { PropertyPath } from "./PropertyPath.js";
6
+ import { Shape } from "./Shape.js";
7
+ import type { ShapesGraph } from "./ShapesGraph.js";
8
+ export declare class PropertyShape extends Shape {
9
+ private readonly shapesGraph;
10
+ readonly constraints: Shape.Constraints;
11
+ constructor(resource: Resource, shapesGraph: ShapesGraph);
12
+ get defaultValue(): Maybe<BlankNode | Literal | NamedNode>;
13
+ get editor(): Maybe<NamedNode>;
14
+ get group(): Maybe<PropertyGroup>;
15
+ get order(): Maybe<number>;
16
+ get path(): PropertyPath;
17
+ get singleLine(): Maybe<boolean>;
18
+ get viewer(): Maybe<NamedNode>;
19
+ toString(): string;
20
+ }
21
+ //# sourceMappingURL=PropertyShape.d.ts.map
@@ -0,0 +1,63 @@
1
+ import { dash, sh } from "@tpluscode/rdf-ns-builders";
2
+ import { PropertyPath } from "./PropertyPath.js";
3
+ import { Shape } from "./Shape.js";
4
+ export class PropertyShape extends Shape {
5
+ constructor(resource, shapesGraph) {
6
+ super(resource);
7
+ this.shapesGraph = shapesGraph;
8
+ this.constraints = new PropertyShape.Constraints(resource, shapesGraph);
9
+ }
10
+ get defaultValue() {
11
+ return this.resource
12
+ .value(sh.defaultValue)
13
+ .map((value) => value.toTerm())
14
+ .toMaybe();
15
+ }
16
+ get editor() {
17
+ return this.resource
18
+ .value(dash.editor)
19
+ .chain((value) => value.toIri())
20
+ .toMaybe();
21
+ }
22
+ get group() {
23
+ return this.resource
24
+ .value(sh.group)
25
+ .chain((value) => value.toIri())
26
+ .toMaybe()
27
+ .chain((node) => this.shapesGraph.propertyGroupByNode(node));
28
+ }
29
+ get order() {
30
+ return this.resource
31
+ .value(sh.maxCount)
32
+ .chain((value) => value.toNumber())
33
+ .toMaybe();
34
+ }
35
+ get path() {
36
+ return this.resource
37
+ .value(sh.path)
38
+ .chain((value) => value.toResource())
39
+ .chain(PropertyPath.fromResource)
40
+ .unsafeCoerce();
41
+ }
42
+ get singleLine() {
43
+ return this.resource
44
+ .value(dash.singleLine)
45
+ .chain((value) => value.toBoolean())
46
+ .toMaybe();
47
+ }
48
+ get viewer() {
49
+ return this.resource
50
+ .value(dash.viewer)
51
+ .chain((value) => value.toIri())
52
+ .toMaybe();
53
+ }
54
+ toString() {
55
+ const keyValues = [`node=${this.resource.identifier.value}`];
56
+ const path = this.path;
57
+ if (path.kind === "PredicatePath") {
58
+ keyValues.push(`path=${path.iri.value}`);
59
+ }
60
+ return `PropertyShape(${keyValues.join(", ")})`;
61
+ }
62
+ }
63
+ //# sourceMappingURL=PropertyShape.js.map
package/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # shacl-ast
2
+
3
+ Build an Abstract Syntax Tree (AST) of [Shapes Constraint Language (SHACL)](https://www.w3.org/TR/shacl/) shapes in an [RDF/JS Dataset](https://rdf.js.org/dataset-spec/).
4
+
5
+ ## Installation
6
+
7
+ npm i @shaclmate/shacl-ast
8
+
9
+ ## Usage
10
+
11
+ ```ts
12
+ import { ShapesGraph } from "@shaclmate/shacl-ast";
13
+
14
+ const shapesGraph = ShapesGraph.fromDataset(testShapesGraph);
15
+ for (const nodeShape of shapesGraph.nodeShapes) {
16
+ console.info("Node shape: ", nodeShape.node.value);
17
+ for (const propertyShape of shapesGraph.propertyShapes) {
18
+ console.info(
19
+ " Property shape: ",
20
+ propertyShape.node.value,
21
+ propertyShape.path.value,
22
+ );
23
+ }
24
+ }
25
+ ```
package/Shape.d.ts ADDED
@@ -0,0 +1,47 @@
1
+ import type { BlankNode, Literal, NamedNode } from "@rdfjs/types";
2
+ import type { Maybe } from "purify-ts";
3
+ import type { Resource } from "rdfjs-resource";
4
+ import { NodeKind } from "./NodeKind.js";
5
+ import type { NodeShape } from "./NodeShape.js";
6
+ import type { ShapesGraph } from "./ShapesGraph.js";
7
+ export declare abstract class Shape {
8
+ readonly resource: Resource;
9
+ abstract readonly constraints: Shape.Constraints;
10
+ readonly targets: Shape.Targets;
11
+ protected constructor(resource: Resource);
12
+ get description(): Maybe<Literal>;
13
+ get name(): Maybe<Literal>;
14
+ }
15
+ export declare namespace Shape {
16
+ class Constraints {
17
+ protected readonly resource: Resource;
18
+ protected readonly shapesGraph: ShapesGraph;
19
+ constructor(resource: Resource, shapesGraph: ShapesGraph);
20
+ get and(): readonly Shape[];
21
+ get classes(): readonly NamedNode[];
22
+ get datatype(): Maybe<NamedNode>;
23
+ get hasValue(): Maybe<BlankNode | Literal | NamedNode>;
24
+ get in_(): Maybe<readonly (BlankNode | Literal | NamedNode)[]>;
25
+ get maxCount(): Maybe<number>;
26
+ get maxExclusive(): Maybe<Literal>;
27
+ get maxInclusive(): Maybe<Literal>;
28
+ get minCount(): Maybe<number>;
29
+ get minExclusive(): Maybe<Literal>;
30
+ get minInclusive(): Maybe<Literal>;
31
+ get nodeKinds(): Set<NodeKind>;
32
+ get nodes(): readonly NodeShape[];
33
+ get not(): readonly Shape[];
34
+ get or(): readonly Shape[];
35
+ get xone(): readonly Shape[];
36
+ private listTakingLogicalConstraint;
37
+ }
38
+ class Targets {
39
+ protected readonly resource: Resource;
40
+ constructor(resource: Resource);
41
+ get targetClasses(): readonly NamedNode[];
42
+ get targetNodes(): readonly (Literal | NamedNode)[];
43
+ get targetObjectsOf(): readonly NamedNode[];
44
+ get targetSubjectsOf(): readonly NamedNode[];
45
+ }
46
+ }
47
+ //# sourceMappingURL=Shape.d.ts.map
package/Shape.js ADDED
@@ -0,0 +1,171 @@
1
+ import { sh } from "@tpluscode/rdf-ns-builders";
2
+ import { NodeKind } from "./NodeKind.js";
3
+ export class Shape {
4
+ constructor(resource) {
5
+ this.resource = resource;
6
+ this.targets = new Shape.Targets(resource);
7
+ }
8
+ get description() {
9
+ return this.resource
10
+ .value(sh.description)
11
+ .chain((value) => value.toLiteral())
12
+ .toMaybe();
13
+ }
14
+ get name() {
15
+ return this.resource
16
+ .value(sh.name)
17
+ .chain((value) => value.toLiteral())
18
+ .toMaybe();
19
+ }
20
+ }
21
+ (function (Shape) {
22
+ class Constraints {
23
+ constructor(resource, shapesGraph) {
24
+ this.resource = resource;
25
+ this.shapesGraph = shapesGraph;
26
+ }
27
+ get and() {
28
+ return this.listTakingLogicalConstraint(sh.and);
29
+ }
30
+ get classes() {
31
+ return [...this.resource.values(sh.class)].flatMap((value) => value.toIri().toMaybe().toList());
32
+ }
33
+ get datatype() {
34
+ return this.resource
35
+ .value(sh.datatype)
36
+ .chain((value) => value.toIri())
37
+ .toMaybe();
38
+ }
39
+ get hasValue() {
40
+ return this.resource
41
+ .value(sh.hasValue)
42
+ .map((value) => value.toTerm())
43
+ .toMaybe();
44
+ }
45
+ get in_() {
46
+ return this.resource
47
+ .value(sh.in)
48
+ .chain((value) => value.toList())
49
+ .map((values) => values.map((value) => value.toTerm()))
50
+ .toMaybe();
51
+ }
52
+ get maxCount() {
53
+ return this.resource
54
+ .value(sh.maxCount)
55
+ .chain((value) => value.toNumber())
56
+ .toMaybe();
57
+ }
58
+ get maxExclusive() {
59
+ return this.resource
60
+ .value(sh.maxExclusive)
61
+ .chain((value) => value.toLiteral())
62
+ .toMaybe();
63
+ }
64
+ get maxInclusive() {
65
+ return this.resource
66
+ .value(sh.maxInclusive)
67
+ .chain((value) => value.toLiteral())
68
+ .toMaybe();
69
+ }
70
+ get minCount() {
71
+ return this.resource
72
+ .value(sh.minCount)
73
+ .chain((value) => value.toNumber())
74
+ .toMaybe();
75
+ }
76
+ get minExclusive() {
77
+ return this.resource
78
+ .value(sh.minExclusive)
79
+ .chain((value) => value.toLiteral())
80
+ .toMaybe();
81
+ }
82
+ get minInclusive() {
83
+ return this.resource
84
+ .value(sh.minInclusive)
85
+ .chain((value) => value.toLiteral())
86
+ .toMaybe();
87
+ }
88
+ get nodeKinds() {
89
+ const nodeKinds = new Set();
90
+ for (const nodeKindValue of this.resource.values(sh.nodeKind)) {
91
+ nodeKindValue.toIri().ifRight((nodeKindIri) => {
92
+ if (nodeKindIri.equals(sh.BlankNode)) {
93
+ nodeKinds.add(NodeKind.BLANK_NODE);
94
+ }
95
+ else if (nodeKindIri.equals(sh.BlankNodeOrIRI)) {
96
+ nodeKinds.add(NodeKind.BLANK_NODE);
97
+ nodeKinds.add(NodeKind.IRI);
98
+ }
99
+ else if (nodeKindIri.equals(sh.BlankNodeOrLiteral)) {
100
+ nodeKinds.add(NodeKind.BLANK_NODE);
101
+ nodeKinds.add(NodeKind.LITERAL);
102
+ }
103
+ else if (nodeKindIri.equals(sh.IRI)) {
104
+ nodeKinds.add(NodeKind.IRI);
105
+ }
106
+ else if (nodeKindIri.equals(sh.IRIOrLiteral)) {
107
+ nodeKinds.add(NodeKind.IRI);
108
+ nodeKinds.add(NodeKind.LITERAL);
109
+ }
110
+ else if (nodeKindIri.equals(sh.Literal)) {
111
+ nodeKinds.add(NodeKind.LITERAL);
112
+ }
113
+ });
114
+ }
115
+ return nodeKinds;
116
+ }
117
+ get nodes() {
118
+ return [...this.resource.values(sh.node)].flatMap((value) => value
119
+ .toIdentifier()
120
+ .toMaybe()
121
+ .chain((shapeNode) => this.shapesGraph.nodeShapeByNode(shapeNode))
122
+ .toList());
123
+ }
124
+ get not() {
125
+ return [...this.resource.values(sh.not)].flatMap((value) => value
126
+ .toIdentifier()
127
+ .toMaybe()
128
+ .chain((shapeNode) => this.shapesGraph.shapeByNode(shapeNode))
129
+ .toList());
130
+ }
131
+ get or() {
132
+ return this.listTakingLogicalConstraint(sh.or);
133
+ }
134
+ get xone() {
135
+ return this.listTakingLogicalConstraint(sh.xone);
136
+ }
137
+ listTakingLogicalConstraint(predicate) {
138
+ return this.resource
139
+ .value(predicate)
140
+ .chain((value) => value.toList())
141
+ .map((values) => values.flatMap((value) => value
142
+ .toIdentifier()
143
+ .toMaybe()
144
+ .chain((shapeNode) => this.shapesGraph.shapeByNode(shapeNode))
145
+ .toList()))
146
+ .orDefault([]);
147
+ }
148
+ }
149
+ Shape.Constraints = Constraints;
150
+ class Targets {
151
+ constructor(resource) {
152
+ this.resource = resource;
153
+ }
154
+ get targetClasses() {
155
+ return [...this.resource.values(sh.targetClass)].flatMap((value) => value.toIri().toMaybe().toList());
156
+ }
157
+ get targetNodes() {
158
+ return [...this.resource.values(sh.targetNode)].flatMap((value) => value.toLiteral().toMaybe()
159
+ .altLazy(() => value.toIri().toMaybe())
160
+ .toList());
161
+ }
162
+ get targetObjectsOf() {
163
+ return [...this.resource.values(sh.targetObjectsOf)].flatMap((value) => value.toIri().toMaybe().toList());
164
+ }
165
+ get targetSubjectsOf() {
166
+ return [...this.resource.values(sh.targetSubjectsOf)].flatMap((value) => value.toIri().toMaybe().toList());
167
+ }
168
+ }
169
+ Shape.Targets = Targets;
170
+ })(Shape || (Shape = {}));
171
+ //# sourceMappingURL=Shape.js.map
@@ -0,0 +1,7 @@
1
+ import type { BlankNode, NamedNode } from "@rdfjs/types";
2
+ import type { ShapesGraph } from "./ShapesGraph.js";
3
+ export interface ShapeParameters {
4
+ node: BlankNode | NamedNode;
5
+ shapesGraph: ShapesGraph;
6
+ }
7
+ //# sourceMappingURL=ShapeParameters.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ShapeParameters.js.map
@@ -0,0 +1,26 @@
1
+ import type { BlankNode, DatasetCore, DefaultGraph, NamedNode } from "@rdfjs/types";
2
+ import { Maybe } from "purify-ts";
3
+ import { NodeShape } from "./NodeShape.js";
4
+ import { PropertyGroup } from "./PropertyGroup.js";
5
+ import { PropertyShape } from "./PropertyShape.js";
6
+ import type { Shape } from "./Shape.js";
7
+ export declare class ShapesGraph {
8
+ readonly dataset: DatasetCore;
9
+ readonly node: BlankNode | DefaultGraph | NamedNode | null;
10
+ readonly nodeShapes: readonly NodeShape[];
11
+ readonly propertyGroups: readonly PropertyGroup[];
12
+ readonly propertyShapes: readonly PropertyShape[];
13
+ private readonly nodeShapesByNode;
14
+ private readonly propertyGroupsByNode;
15
+ private readonly propertyShapesByNode;
16
+ private constructor();
17
+ static fromDataset(dataset: DatasetCore): ShapesGraph;
18
+ nodeShapeByNode(nodeShapeNode: BlankNode | NamedNode): Maybe<NodeShape>;
19
+ propertyGroupByNode(propertyGroupNode: NamedNode): Maybe<PropertyGroup>;
20
+ propertyShapeByNode(propertyShapeNode: BlankNode | NamedNode): Maybe<PropertyShape>;
21
+ shapeByNode(node: BlankNode | NamedNode): Maybe<Shape>;
22
+ private readGraph;
23
+ private readPropertyGroups;
24
+ private readShapes;
25
+ }
26
+ //# sourceMappingURL=ShapesGraph.d.ts.map
package/ShapesGraph.js ADDED
@@ -0,0 +1,201 @@
1
+ import TermMap from "@rdfjs/term-map";
2
+ import TermSet from "@rdfjs/term-set";
3
+ import { rdf, sh } from "@tpluscode/rdf-ns-builders";
4
+ import { Maybe } from "purify-ts";
5
+ import { Resource, ResourceSet } from "rdfjs-resource";
6
+ import { NodeShape } from "./NodeShape.js";
7
+ import { PropertyGroup } from "./PropertyGroup.js";
8
+ import { PropertyShape } from "./PropertyShape.js";
9
+ export class ShapesGraph {
10
+ constructor(dataset) {
11
+ this.dataset = dataset;
12
+ this.node = this.readGraph();
13
+ const { nodeShapes, nodeShapesByNode, propertyShapes, propertyShapesByNode, } = this.readShapes();
14
+ this.nodeShapes = nodeShapes;
15
+ this.nodeShapesByNode = nodeShapesByNode;
16
+ this.propertyShapes = propertyShapes;
17
+ this.propertyShapesByNode = propertyShapesByNode;
18
+ const { propertyGroups, propertyGroupsByNode } = this.readPropertyGroups();
19
+ this.propertyGroups = propertyGroups;
20
+ this.propertyGroupsByNode = propertyGroupsByNode;
21
+ }
22
+ static fromDataset(dataset) {
23
+ return new ShapesGraph(dataset);
24
+ }
25
+ nodeShapeByNode(nodeShapeNode) {
26
+ return Maybe.fromNullable(this.nodeShapesByNode.get(nodeShapeNode));
27
+ }
28
+ propertyGroupByNode(propertyGroupNode) {
29
+ return Maybe.fromNullable(this.propertyGroupsByNode.get(propertyGroupNode));
30
+ }
31
+ propertyShapeByNode(propertyShapeNode) {
32
+ return Maybe.fromNullable(this.propertyShapesByNode.get(propertyShapeNode));
33
+ }
34
+ shapeByNode(node) {
35
+ const nodeShape = this.nodeShapeByNode(node);
36
+ if (nodeShape.isJust()) {
37
+ return nodeShape;
38
+ }
39
+ return this.propertyShapeByNode(node);
40
+ }
41
+ readGraph() {
42
+ const graphs = new TermSet();
43
+ for (const quad of this.dataset) {
44
+ graphs.add(quad.graph);
45
+ }
46
+ if (graphs.size !== 1) {
47
+ return null;
48
+ }
49
+ const graph = [...graphs.values()][0];
50
+ switch (graph.termType) {
51
+ case "BlankNode":
52
+ case "DefaultGraph":
53
+ case "NamedNode":
54
+ return graph;
55
+ default:
56
+ throw new RangeError(`expected NamedNode or default graph, actual ${graph.termType}`);
57
+ }
58
+ }
59
+ readPropertyGroups() {
60
+ const propertyGroups = [];
61
+ const propertyGroupsByNode = new TermMap();
62
+ for (const quad of this.dataset.match(null, rdf.type, sh.PropertyGroup, this.node)) {
63
+ const subject = quad.subject;
64
+ if (subject.termType !== "NamedNode") {
65
+ continue;
66
+ }
67
+ if (propertyGroupsByNode.has(subject)) {
68
+ continue;
69
+ }
70
+ const propertyGroup = new PropertyGroup(new Resource({ dataset: this.dataset, identifier: subject }));
71
+ propertyGroups.push(propertyGroup);
72
+ propertyGroupsByNode.set(subject, propertyGroup);
73
+ }
74
+ return { propertyGroups, propertyGroupsByNode };
75
+ }
76
+ readShapes() {
77
+ const resourceSet = new ResourceSet({ dataset: this.dataset });
78
+ // Collect the shape identifiers in sets
79
+ const shapeNodeSet = new TermSet();
80
+ // Utility function for doing the collection
81
+ const addShapeNode = (shapeNode) => {
82
+ switch (shapeNode.termType) {
83
+ case "BlankNode":
84
+ case "NamedNode":
85
+ shapeNodeSet.add(shapeNode);
86
+ break;
87
+ }
88
+ };
89
+ // Test each shape condition
90
+ // https://www.w3.org/TR/shacl/#shapes
91
+ // Subject is a SHACL instance of sh:NodeShape or sh:PropertyShape
92
+ for (const rdfType of [sh.NodeShape, sh.PropertyShape]) {
93
+ for (const resource of resourceSet.instancesOf(rdfType, {
94
+ graph: this.node,
95
+ })) {
96
+ addShapeNode(resource.identifier);
97
+ }
98
+ }
99
+ // Subject of a triple with sh:targetClass, sh:targetNode, sh:targetObjectsOf, or sh:targetSubjectsOf predicate
100
+ for (const predicate of [
101
+ sh.targetClass,
102
+ sh.targetNode,
103
+ sh.targetObjectsOf,
104
+ sh.targetSubjectsOf,
105
+ ]) {
106
+ for (const quad of this.dataset.match(null, predicate, null, this.node)) {
107
+ addShapeNode(quad.subject);
108
+ }
109
+ }
110
+ // Subject of a triple that has a parameter as predicate
111
+ // https://www.w3.org/TR/shacl/#constraints
112
+ // https://www.w3.org/TR/shacl/#core-components
113
+ for (const predicate of [
114
+ sh.class,
115
+ sh.datatype,
116
+ sh.nodeKind,
117
+ sh.minCount,
118
+ sh.maxCount,
119
+ sh.minExclusive,
120
+ sh.minInclusive,
121
+ sh.maxExclusive,
122
+ sh.maxInclusive,
123
+ sh.minLength,
124
+ sh.maxLength,
125
+ sh.pattern,
126
+ sh.languageIn,
127
+ sh.uniqueLang,
128
+ sh.equals,
129
+ sh.disjoint,
130
+ sh.lessThan,
131
+ sh.lessThanOrEquals,
132
+ sh.not,
133
+ sh.and,
134
+ sh.or,
135
+ sh.xone,
136
+ sh.node,
137
+ sh.property,
138
+ sh.qualifiedValueShape,
139
+ sh.qualifiedMinCount,
140
+ sh.qualifiedMaxCount,
141
+ sh.closed,
142
+ sh.ignoredProperties,
143
+ sh.hasValue,
144
+ sh.in,
145
+ ]) {
146
+ for (const quad of this.dataset.match(null, predicate, null, this.node)) {
147
+ addShapeNode(quad.subject);
148
+ }
149
+ }
150
+ // Object of a shape-expecting, non-list-taking parameter such as sh:node
151
+ for (const predicate of [sh.node, sh.property]) {
152
+ for (const quad of this.dataset.match(null, predicate, null, this.node)) {
153
+ addShapeNode(quad.object);
154
+ }
155
+ }
156
+ // Member of a SHACL list that is a value of a shape-expecting and list-taking parameter such as sh:or
157
+ for (const predicate of [sh.and, sh["in"], sh.languageIn, sh.or, sh.xone]) {
158
+ for (const quad of this.dataset.match(null, predicate, null, this.node)) {
159
+ switch (quad.object.termType) {
160
+ case "BlankNode":
161
+ case "NamedNode":
162
+ break;
163
+ default:
164
+ continue;
165
+ }
166
+ for (const value of resourceSet
167
+ .resource(quad.object)
168
+ .toList()
169
+ .orDefault([])) {
170
+ value.toIdentifier().ifRight(addShapeNode);
171
+ }
172
+ }
173
+ }
174
+ // Separate shapes into node and property shapes.
175
+ const nodeShapes = [];
176
+ const nodeShapesByNode = new TermMap();
177
+ const propertyShapes = [];
178
+ const propertyShapesByNode = new TermMap();
179
+ for (const shapeNode of shapeNodeSet) {
180
+ if (this.dataset.match(shapeNode, sh.path, null, this.node).size > 0) {
181
+ // A property shape is a shape in the shapes graph that is the subject of a triple that has sh:path as its predicate. A shape has at most one value for sh:path. Each value of sh:path in a shape must be a well-formed SHACL property path. It is recommended, but not required, for a property shape to be declared as a SHACL instance of sh:PropertyShape. SHACL instances of sh:PropertyShape have one value for the property sh:path.
182
+ const propertyShape = new PropertyShape(resourceSet.resource(shapeNode), this);
183
+ propertyShapes.push(propertyShape);
184
+ propertyShapesByNode.set(shapeNode, propertyShape);
185
+ }
186
+ else {
187
+ // A node shape is a shape in the shapes graph that is not the subject of a triple with sh:path as its predicate. It is recommended, but not required, for a node shape to be declared as a SHACL instance of sh:NodeShape. SHACL instances of sh:NodeShape cannot have a value for the property sh:path.
188
+ const nodeShape = new NodeShape(resourceSet.resource(shapeNode), this);
189
+ nodeShapes.push(nodeShape);
190
+ nodeShapesByNode.set(shapeNode, nodeShape);
191
+ }
192
+ }
193
+ return {
194
+ nodeShapes,
195
+ nodeShapesByNode,
196
+ propertyShapes,
197
+ propertyShapesByNode,
198
+ };
199
+ }
200
+ }
201
+ //# sourceMappingURL=ShapesGraph.js.map
package/index.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ export * from "./NodeKind.js";
2
+ export * from "./NodeShape.js";
3
+ export * from "./PropertyGroup.js";
4
+ export * from "./PropertyPath.js";
5
+ export * from "./PropertyShape.js";
6
+ export * from "./Shape.js";
7
+ export * from "./ShapesGraph.js";
8
+ //# sourceMappingURL=index.d.ts.map
package/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export * from "./NodeKind.js";
2
+ export * from "./NodeShape.js";
3
+ export * from "./PropertyGroup.js";
4
+ export * from "./PropertyPath.js";
5
+ export * from "./PropertyShape.js";
6
+ export * from "./Shape.js";
7
+ export * from "./ShapesGraph.js";
8
+ //# sourceMappingURL=index.js.map
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "dependencies": {
3
+ "@rdfjs/term-map": "^2.0.2",
4
+ "@rdfjs/term-set": "^2.0.3",
5
+ "@rdfjs/types": "^1.1.0",
6
+ "@tpluscode/rdf-ns-builders": "^4.3.0",
7
+ "@types/rdfjs__term-map": "^2.0.10",
8
+ "@types/rdfjs__term-set": "^2.0.9",
9
+ "purify-ts": "^2.1.0",
10
+ "rdfjs-resource": "1.0.12"
11
+ },
12
+ "devDependencies": {
13
+ "@types/n3": "^1.21.1",
14
+ "n3": "^1.21.3"
15
+ },
16
+ "files": [
17
+ "*.d.ts",
18
+ "*.js"
19
+ ],
20
+ "main": "index.js",
21
+ "license": "Apache-2.0",
22
+ "name": "@shaclmate/shacl-ast",
23
+ "scripts": {
24
+ "build": "tsc -b",
25
+ "check": "biome check",
26
+ "check:write": "biome check --write",
27
+ "check:write:unsafe": "biome check --write --unsafe",
28
+ "clean": "rimraf -g **/*.d.ts* **/*.js **/*.js.map tsconfig.tsbuildinfo",
29
+ "format": "biome format",
30
+ "format:write": "biome format --write",
31
+ "format:write:unsafe": "biome format --write --unsafe",
32
+ "rebuild": "run-s clean build",
33
+ "link-dependencies": "npm link purify-ts-helpers rdfjs-resource",
34
+ "lint": "biome lint",
35
+ "lint:write": "biome lint --write",
36
+ "lint:write:unsafe": "biome lint --write --unsafe",
37
+ "test": "biome check && vitest run",
38
+ "test:coverage": "biome check && vitest run --coverage",
39
+ "test:watch": "vitest watch",
40
+ "unlink": "npm unlink -g @shaclmate/shacl-ast",
41
+ "watch": "tsc -w --preserveWatchOutput"
42
+ },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "git+https://github.com/minorg/shaclmate"
46
+ },
47
+ "type": "module",
48
+ "types": "index.d.ts",
49
+ "version": "2.0.13"
50
+ }