@ulb-darmstadt/shacl-form 1.9.4 → 1.10.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 +1 -5
- package/dist/config.d.ts +1 -0
- package/dist/constants.d.ts +1 -0
- package/dist/exports.d.ts +1 -1
- package/dist/form-bootstrap.js +91 -69
- package/dist/form-default.js +88 -66
- package/dist/form-material.js +128 -106
- package/dist/form.d.ts +0 -1
- package/dist/loader.d.ts +3 -2
- package/dist/plugins/leaflet.js +2 -2
- package/dist/plugins/mapbox.js +15 -15
- package/package.json +3 -3
- package/src/config.ts +1 -0
- package/src/constants.ts +1 -0
- package/src/exports.ts +1 -1
- package/src/form.ts +0 -6
- package/src/loader.ts +91 -65
- package/src/node.ts +2 -2
- package/src/property.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ulb-darmstadt/shacl-form",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "SHACL form generator",
|
|
5
5
|
"main": "dist/form-default.js",
|
|
6
6
|
"module": "dist/form-default.js",
|
|
@@ -69,7 +69,6 @@
|
|
|
69
69
|
"leaflet": "^1.9.4",
|
|
70
70
|
"leaflet-editable": "^1.3.2",
|
|
71
71
|
"leaflet.fullscreen": "^4.0.0",
|
|
72
|
-
"lit": "^3.3.1",
|
|
73
72
|
"mapbox-gl": "^3.14.0",
|
|
74
73
|
"n3": "^1.26.0",
|
|
75
74
|
"rdfxml-streaming-parser": "^3.1.0",
|
|
@@ -77,7 +76,8 @@
|
|
|
77
76
|
"uuid": "^11.1.0"
|
|
78
77
|
},
|
|
79
78
|
"peerDependencies": {
|
|
80
|
-
"
|
|
79
|
+
"lit": "^3.3.1",
|
|
80
|
+
"@ro-kit/ui-widgets": "^0.0.43",
|
|
81
81
|
"mdui": "^2.1.4"
|
|
82
82
|
}
|
|
83
83
|
}
|
package/src/config.ts
CHANGED
|
@@ -22,6 +22,7 @@ export class ElementAttributes {
|
|
|
22
22
|
view: string | null = null
|
|
23
23
|
language: string | null = null
|
|
24
24
|
loading: string = 'Loading\u2026'
|
|
25
|
+
proxy: string | null = null
|
|
25
26
|
ignoreOwlImports: string | null = null
|
|
26
27
|
collapse: string | null = null
|
|
27
28
|
submitButton: string | null = null
|
package/src/constants.ts
CHANGED
|
@@ -24,6 +24,7 @@ export const SHACL_OBJECT_NODE_SHAPE = DataFactory.namedNode(PREFIX_SHACL + 'Nod
|
|
|
24
24
|
export const SHACL_OBJECT_IRI = DataFactory.namedNode(PREFIX_SHACL + 'IRI')
|
|
25
25
|
export const SHACL_PREDICATE_PROPERTY = DataFactory.namedNode(PREFIX_SHACL + 'property')
|
|
26
26
|
export const SHACL_PREDICATE_CLASS = DataFactory.namedNode(PREFIX_SHACL + 'class')
|
|
27
|
+
export const SHACL_PREDICATE_NODE = DataFactory.namedNode(PREFIX_SHACL + 'node')
|
|
27
28
|
export const SHACL_PREDICATE_TARGET_CLASS = DataFactory.namedNode(PREFIX_SHACL + 'targetClass')
|
|
28
29
|
export const SHACL_PREDICATE_NODE_KIND = DataFactory.namedNode(PREFIX_SHACL + 'nodeKind')
|
|
29
30
|
export const XSD_DATATYPE_STRING = DataFactory.namedNode(PREFIX_XSD + 'string')
|
package/src/exports.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type { InputListEntry, Editor } from './theme'
|
|
2
2
|
export { Theme } from './theme'
|
|
3
|
-
export { Loader
|
|
3
|
+
export { Loader } from './loader'
|
|
4
4
|
export { Config } from './config'
|
|
5
5
|
export { Plugin, registerPlugin } from './plugin'
|
|
6
6
|
export { ShaclPropertyTemplate } from './property-template'
|
package/src/form.ts
CHANGED
|
@@ -6,7 +6,6 @@ import { DATA_GRAPH, DCTERMS_PREDICATE_CONFORMS_TO, PREFIX_SHACL, RDF_PREDICATE_
|
|
|
6
6
|
import { Editor, Theme } from './theme'
|
|
7
7
|
import { serialize } from './serialize'
|
|
8
8
|
import { Validator } from 'shacl-engine'
|
|
9
|
-
import { setSharedShapesGraph } from './loader'
|
|
10
9
|
import { RokitCollapsible } from '@ro-kit/ui-widgets'
|
|
11
10
|
|
|
12
11
|
export class ShaclForm extends HTMLElement {
|
|
@@ -140,11 +139,6 @@ export class ShaclForm extends HTMLElement {
|
|
|
140
139
|
this.initialize()
|
|
141
140
|
}
|
|
142
141
|
|
|
143
|
-
public setSharedShapesGraph(graph: Store) {
|
|
144
|
-
setSharedShapesGraph(graph)
|
|
145
|
-
this.initialize()
|
|
146
|
-
}
|
|
147
|
-
|
|
148
142
|
public setClassInstanceProvider(provider: ClassInstanceProvider) {
|
|
149
143
|
this.config.classInstanceProvider = provider
|
|
150
144
|
this.initialize()
|
package/src/loader.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Store, Quad, NamedNode, DataFactory, StreamParser } from 'n3'
|
|
2
|
-
import { DATA_GRAPH, DCTERMS_PREDICATE_CONFORMS_TO, OWL_PREDICATE_IMPORTS,
|
|
1
|
+
import { Store, Quad, NamedNode, DataFactory, StreamParser, Term } from 'n3'
|
|
2
|
+
import { DATA_GRAPH, DCTERMS_PREDICATE_CONFORMS_TO, OWL_PREDICATE_IMPORTS, SHACL_PREDICATE_CLASS, SHACL_PREDICATE_NODE, SHACL_PREDICATE_TARGET_CLASS, SHAPES_GRAPH } from './constants'
|
|
3
3
|
import { Config } from './config'
|
|
4
4
|
import { isURL } from './util'
|
|
5
5
|
import { RdfXmlParser } from 'rdfxml-streaming-parser'
|
|
@@ -11,12 +11,10 @@ import { toRDF } from 'jsonld'
|
|
|
11
11
|
// that import the same resources
|
|
12
12
|
const loadedURLCache: Record<string, Promise<string>> = {}
|
|
13
13
|
const loadedClassesCache: Record<string, Promise<string>> = {}
|
|
14
|
-
let sharedShapesGraph: Store | undefined
|
|
15
14
|
|
|
16
15
|
export class Loader {
|
|
17
16
|
private config: Config
|
|
18
17
|
private loadedExternalUrls: string[] = []
|
|
19
|
-
private loadedClasses: string[] = []
|
|
20
18
|
|
|
21
19
|
constructor(config: Config) {
|
|
22
20
|
this.config = config
|
|
@@ -25,33 +23,35 @@ export class Loader {
|
|
|
25
23
|
async loadGraphs() {
|
|
26
24
|
// clear local caches
|
|
27
25
|
this.loadedExternalUrls = []
|
|
28
|
-
this.loadedClasses = []
|
|
29
|
-
|
|
30
|
-
let store = sharedShapesGraph
|
|
31
26
|
this.config.prefixes = {}
|
|
32
27
|
|
|
33
28
|
const promises: Promise<void>[] = []
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
promises.push(this.importRDF(this.config.attributes.values ? this.config.attributes.values : this.config.attributes.valuesUrl ? fetchRDF(this.config.attributes.valuesUrl) : '', store, DATA_GRAPH))
|
|
29
|
+
const store = new Store()
|
|
30
|
+
promises.push(this.importRDF(this.config.attributes.shapes ? this.config.attributes.shapes : this.config.attributes.shapesUrl ? this.fetchRDF(this.config.attributes.shapesUrl) : '', store, SHAPES_GRAPH))
|
|
31
|
+
// load data graph
|
|
32
|
+
promises.push(this.importRDF(this.config.attributes.values ? this.config.attributes.values : this.config.attributes.valuesUrl ? this.fetchRDF(this.config.attributes.valuesUrl) : '', store, DATA_GRAPH))
|
|
39
33
|
await Promise.all(promises)
|
|
34
|
+
await this.fetchOwlImports(store)
|
|
35
|
+
await this.fetchClassInstances(store)
|
|
40
36
|
|
|
41
37
|
// if shapes graph is empty, but we have the following triples:
|
|
42
38
|
// <valueSubject> a <uri> or <valueSubject> dcterms:conformsTo <uri>
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
// or if we have data-shape-subject set on the form,
|
|
40
|
+
// then try to load the referenced object(s) into the shapes graph
|
|
41
|
+
if (store.countQuads(null, null, null, SHAPES_GRAPH) === 0) {
|
|
42
|
+
const shapeCandidates = new Set<Term>()
|
|
43
|
+
if (this.config.attributes.valuesSubject) {
|
|
44
|
+
store.forObjects((object) => shapeCandidates.add(object), this.config.attributes.valuesSubject, DCTERMS_PREDICATE_CONFORMS_TO, DATA_GRAPH)
|
|
45
|
+
}
|
|
46
|
+
if (this.config.attributes.shapeSubject) {
|
|
47
|
+
shapeCandidates.add(DataFactory.namedNode(this.config.attributes.shapeSubject))
|
|
48
|
+
}
|
|
49
49
|
const promises: Promise<void>[] = []
|
|
50
50
|
for (const uri of shapeCandidates) {
|
|
51
51
|
const url = this.toURL(uri.value)
|
|
52
52
|
if (url && this.loadedExternalUrls.indexOf(url) < 0) {
|
|
53
53
|
this.loadedExternalUrls.push(url)
|
|
54
|
-
promises.push(this.importRDF(fetchRDF(url), store, SHAPES_GRAPH))
|
|
54
|
+
promises.push(this.importRDF(this.fetchRDF(url), store, SHAPES_GRAPH))
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
try {
|
|
@@ -71,33 +71,6 @@ export class Loader {
|
|
|
71
71
|
const parser = guessContentType(input) === 'xml' ? new RdfXmlParser() : new StreamParser()
|
|
72
72
|
parser.on('data', (quad: Quad) => {
|
|
73
73
|
store.add(new Quad(quad.subject, quad.predicate, quad.object, graph))
|
|
74
|
-
// check if this is an owl:imports predicate and try to load the url
|
|
75
|
-
if (this.config.attributes.ignoreOwlImports === null && OWL_PREDICATE_IMPORTS.equals(quad.predicate)) {
|
|
76
|
-
const url = this.toURL(quad.object.value)
|
|
77
|
-
// import url only once
|
|
78
|
-
if (url && this.loadedExternalUrls.indexOf(url) < 0) {
|
|
79
|
-
this.loadedExternalUrls.push(url)
|
|
80
|
-
// import into separate graph
|
|
81
|
-
dependencies.push(this.importRDF(fetchRDF(url), store, DataFactory.namedNode(url)))
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
// check if this is an sh:class predicate and invoke class instance provider
|
|
85
|
-
if (this.config.classInstanceProvider && SHACL_PREDICATE_CLASS.equals(quad.predicate)) {
|
|
86
|
-
const className = quad.object.value
|
|
87
|
-
// import class definitions only once
|
|
88
|
-
if (this.loadedClasses.indexOf(className) < 0) {
|
|
89
|
-
let promise: Promise<string>
|
|
90
|
-
// check if class is in module scope cache
|
|
91
|
-
if (className in loadedClassesCache) {
|
|
92
|
-
promise = loadedClassesCache[className]
|
|
93
|
-
} else {
|
|
94
|
-
promise = this.config.classInstanceProvider(className)
|
|
95
|
-
loadedClassesCache[className] = promise
|
|
96
|
-
}
|
|
97
|
-
this.loadedClasses.push(className)
|
|
98
|
-
dependencies.push(this.importRDF(promise, store, graph))
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
74
|
})
|
|
102
75
|
.on('error', (error) => {
|
|
103
76
|
console.warn('failed parsing graph', graph, error.message)
|
|
@@ -138,6 +111,78 @@ export class Loader {
|
|
|
138
111
|
}
|
|
139
112
|
}
|
|
140
113
|
|
|
114
|
+
async fetchRDF(url: string): Promise<string> {
|
|
115
|
+
// try to load from cache first
|
|
116
|
+
if (url in loadedURLCache) {
|
|
117
|
+
console.log('--- cache hit', url)
|
|
118
|
+
return loadedURLCache[url]
|
|
119
|
+
}
|
|
120
|
+
let proxiedURL = url
|
|
121
|
+
// if we have a proxy configured, then load url via proxy
|
|
122
|
+
if (this.config.attributes.proxy) {
|
|
123
|
+
proxiedURL = this.config.attributes.proxy + encodeURIComponent(url)
|
|
124
|
+
}
|
|
125
|
+
const promise = fetch(proxiedURL, {
|
|
126
|
+
headers: {
|
|
127
|
+
'Accept': 'text/turtle, application/trig, application/n-triples, application/n-quads, text/n3, application/ld+json'
|
|
128
|
+
},
|
|
129
|
+
}).then(resp => resp.text())
|
|
130
|
+
loadedURLCache[url] = promise
|
|
131
|
+
return promise
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async fetchOwlImports(store: Store) {
|
|
135
|
+
if (this.config.attributes.ignoreOwlImports === null) {
|
|
136
|
+
const urls = new Set<string>()
|
|
137
|
+
// find all triples in all graphs of the form <s> <sh:class> <:className>
|
|
138
|
+
store.forObjects((url) => {
|
|
139
|
+
urls.add(url.value)
|
|
140
|
+
}, null, OWL_PREDICATE_IMPORTS, null)
|
|
141
|
+
|
|
142
|
+
const dependencies: Promise<void>[] = []
|
|
143
|
+
for (const url of urls) {
|
|
144
|
+
const convertedURL = this.toURL(url)
|
|
145
|
+
// import url only once
|
|
146
|
+
if (convertedURL) {
|
|
147
|
+
// import into separate graph
|
|
148
|
+
dependencies.push(this.importRDF(this.fetchRDF(convertedURL), store, DataFactory.namedNode(convertedURL)))
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return Promise.allSettled(dependencies)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async fetchClassInstances(store: Store) {
|
|
156
|
+
if (this.config.classInstanceProvider) {
|
|
157
|
+
const classNames = new Set<string>()
|
|
158
|
+
// find all triples in all graphs of the form <s> <sh:class> <:className>
|
|
159
|
+
store.forObjects((clazz) => {
|
|
160
|
+
classNames.add(clazz.value)
|
|
161
|
+
}, null, SHACL_PREDICATE_CLASS, null)
|
|
162
|
+
// find all triples in all graphs of the form <s> <sh:node> <o> and <o> <sh:targetClass> <:className>
|
|
163
|
+
store.forObjects((node) => {
|
|
164
|
+
store.forObjects((clazz) => {
|
|
165
|
+
classNames.add(clazz.value)
|
|
166
|
+
}, node, SHACL_PREDICATE_TARGET_CLASS, null)
|
|
167
|
+
}, null, SHACL_PREDICATE_NODE, null)
|
|
168
|
+
|
|
169
|
+
const dependencies: Promise<void>[] = []
|
|
170
|
+
for (const className of classNames) {
|
|
171
|
+
let promise: Promise<string>
|
|
172
|
+
// check if class is in module scope cache
|
|
173
|
+
if (className in loadedClassesCache) {
|
|
174
|
+
console.log('--- class cache hit', className)
|
|
175
|
+
promise = loadedClassesCache[className]
|
|
176
|
+
} else {
|
|
177
|
+
promise = this.config.classInstanceProvider(className)
|
|
178
|
+
loadedClassesCache[className] = promise
|
|
179
|
+
}
|
|
180
|
+
dependencies.push(this.importRDF(promise, store, SHAPES_GRAPH))
|
|
181
|
+
}
|
|
182
|
+
return Promise.all(dependencies)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
141
186
|
toURL(id: string): string | null {
|
|
142
187
|
if (isURL(id)) {
|
|
143
188
|
return id
|
|
@@ -158,21 +203,6 @@ export class Loader {
|
|
|
158
203
|
}
|
|
159
204
|
return null
|
|
160
205
|
}
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
async function fetchRDF(url: string): Promise<string> {
|
|
165
|
-
// try to load from cache first
|
|
166
|
-
if (url in loadedURLCache) {
|
|
167
|
-
return loadedURLCache[url]
|
|
168
|
-
}
|
|
169
|
-
const promise = fetch(url, {
|
|
170
|
-
headers: {
|
|
171
|
-
'Accept': 'text/turtle, application/trig, application/n-triples, application/n-quads, text/n3, application/ld+json'
|
|
172
|
-
},
|
|
173
|
-
}).then(resp => resp.text())
|
|
174
|
-
loadedURLCache[url] = promise
|
|
175
|
-
return promise
|
|
176
206
|
}
|
|
177
207
|
|
|
178
208
|
/* Can't rely on HTTP content-type header, since many resources are delivered with text/plain */
|
|
@@ -184,7 +214,3 @@ function guessContentType(input: string) {
|
|
|
184
214
|
}
|
|
185
215
|
return 'ttl'
|
|
186
216
|
}
|
|
187
|
-
|
|
188
|
-
export function setSharedShapesGraph(graph: Store) {
|
|
189
|
-
sharedShapesGraph = graph
|
|
190
|
-
}
|
package/src/node.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BlankNode, DataFactory, NamedNode, Store } from 'n3'
|
|
2
2
|
import { Term } from '@rdfjs/types'
|
|
3
|
-
import { PREFIX_SHACL, RDF_PREDICATE_TYPE, OWL_PREDICATE_IMPORTS, SHACL_PREDICATE_PROPERTY } from './constants'
|
|
3
|
+
import { PREFIX_SHACL, RDF_PREDICATE_TYPE, OWL_PREDICATE_IMPORTS, SHACL_PREDICATE_PROPERTY, SHACL_PREDICATE_NODE } from './constants'
|
|
4
4
|
import { ShaclProperty } from './property'
|
|
5
5
|
import { createShaclGroup } from './group'
|
|
6
6
|
import { v4 as uuidv4 } from 'uuid'
|
|
@@ -97,7 +97,7 @@ export class ShaclNode extends HTMLElement {
|
|
|
97
97
|
console.error('list not found:', quad.object.value, 'existing lists:', config.lists)
|
|
98
98
|
}
|
|
99
99
|
break;
|
|
100
|
-
case
|
|
100
|
+
case SHACL_PREDICATE_NODE.id:
|
|
101
101
|
// inheritance via sh:node
|
|
102
102
|
this.prepend(new ShaclNode(quad.object as NamedNode, config, valueSubject, this))
|
|
103
103
|
break;
|
package/src/property.ts
CHANGED
|
@@ -192,6 +192,7 @@ export class ShaclProperty extends HTMLElement {
|
|
|
192
192
|
addButton.dense = true
|
|
193
193
|
addButton.label = "+ " + this.template.label
|
|
194
194
|
addButton.title = 'Add ' + this.template.label
|
|
195
|
+
addButton.autoGrowLabelWidth = true
|
|
195
196
|
addButton.classList.add('add-button')
|
|
196
197
|
|
|
197
198
|
// load potential value candidates for linking
|