@ulb-darmstadt/shacl-form 1.9.0 → 1.9.2

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/dist/util.d.ts CHANGED
@@ -11,3 +11,7 @@ export declare function removePrefixes(id: string, prefixes: Prefixes): string;
11
11
  export declare function findInstancesOf(clazz: NamedNode, template: ShaclPropertyTemplate): InputListEntry[];
12
12
  export declare function isURL(input: string): boolean;
13
13
  export declare function prioritizeByLanguage(languages: string[], text1?: Literal, text2?: Literal): Literal | undefined;
14
+ export declare function extractLists(store: Store, { remove, ignoreErrors }?: {
15
+ remove?: boolean | undefined;
16
+ ignoreErrors?: boolean | undefined;
17
+ }): Record<string, Term[]>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ulb-darmstadt/shacl-form",
3
- "version": "1.9.0",
3
+ "version": "1.9.2",
4
4
  "description": "SHACL form generator",
5
5
  "main": "dist/form-default.js",
6
6
  "module": "dist/form-default.js",
@@ -55,7 +55,7 @@
55
55
  "@types/uuid": "^10.0.0",
56
56
  "rollup-plugin-peer-deps-external": "^2.2.4",
57
57
  "typescript": "^5.8.3",
58
- "vite": "^7.0.5",
58
+ "vite": "^7.0.6",
59
59
  "vite-plugin-dts": "^4.5.4"
60
60
  },
61
61
  "dependencies": {
@@ -63,7 +63,7 @@
63
63
  "bootstrap": "^5.3.7",
64
64
  "jsonld": "^8.3.3",
65
65
  "leaflet": "^1.9.4",
66
- "leaflet-editable": "^1.3.1",
66
+ "leaflet-editable": "^1.3.2",
67
67
  "leaflet.fullscreen": "^4.0.0",
68
68
  "mapbox-gl": "^3.13.0",
69
69
  "n3": "^1.26.0",
@@ -72,7 +72,7 @@
72
72
  "uuid": "^11.1.0"
73
73
  },
74
74
  "peerDependencies": {
75
- "@ro-kit/ui-widgets": "^0.0.33",
75
+ "@ro-kit/ui-widgets": "^0.0.34",
76
76
  "mdui": "^2.1.4"
77
77
  }
78
78
  }
package/src/config.ts CHANGED
@@ -4,6 +4,7 @@ import { PREFIX_SHACL, RDF_PREDICATE_TYPE } from './constants'
4
4
  import { ClassInstanceProvider } from './plugin'
5
5
  import { Loader } from './loader'
6
6
  import { Theme } from './theme'
7
+ import { extractLists } from './util'
7
8
 
8
9
  export class ElementAttributes {
9
10
  shapes: string | null = null
@@ -99,19 +100,10 @@ export class Config {
99
100
 
100
101
  set store(store: Store) {
101
102
  this._store = store
102
- this.lists = store.extractLists()
103
+ this.lists = extractLists(store)
103
104
  this.groups = []
104
105
  store.forSubjects(subject => {
105
106
  this.groups.push(subject.id)
106
107
  }, RDF_PREDICATE_TYPE, `${PREFIX_SHACL}PropertyGroup`, null)
107
108
  }
108
-
109
- registerPrefixes(prefixes: Prefixes) {
110
- for (const key in prefixes) {
111
- // ignore empty (default) namespace
112
- if (key) {
113
- this.prefixes[key] = prefixes[key]
114
- }
115
- }
116
- }
117
109
  }
package/src/loader.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Store, Quad, NamedNode, DataFactory, Parser, Prefixes } from 'n3'
1
+ import { Store, Quad, NamedNode, DataFactory, StreamParser } from 'n3'
2
2
  import { DATA_GRAPH, DCTERMS_PREDICATE_CONFORMS_TO, OWL_PREDICATE_IMPORTS, RDF_PREDICATE_TYPE, SHACL_PREDICATE_CLASS, SHAPES_GRAPH } from './constants'
3
3
  import { Config } from './config'
4
4
  import { isURL } from './util'
@@ -11,7 +11,6 @@ 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
- const ttlParser = new Parser()
15
14
  let sharedShapesGraph: Store | undefined
16
15
 
17
16
  export class Loader {
@@ -69,7 +68,8 @@ export class Loader {
69
68
  const parse = async (input: string) => {
70
69
  const dependencies: Promise<void>[] = []
71
70
  await new Promise((resolve, reject) => {
72
- const addQuad = (quad: Quad) => {
71
+ const parser = guessContentType(input) === 'xml' ? new RdfXmlParser() : new StreamParser()
72
+ parser.on('data', (quad: Quad) => {
73
73
  store.add(new Quad(quad.subject, quad.predicate, quad.object, graph))
74
74
  // check if this is an owl:imports predicate and try to load the url
75
75
  if (this.config.attributes.ignoreOwlImports === null && OWL_PREDICATE_IMPORTS.equals(quad.predicate)) {
@@ -98,44 +98,22 @@ export class Loader {
98
98
  dependencies.push(this.importRDF(promise, store, graph))
99
99
  }
100
100
  }
101
- }
102
- const type = guessContentType(input)
103
- if (type === 'xml') {
104
- const parser = new RdfXmlParser()
105
- parser.on('data', (quad: Quad) => {
106
- addQuad(quad)
107
- })
108
- .on('error', (error) => {
109
- console.warn('failed parsing graph', graph, error.message)
110
- reject(error)
111
- })
112
- .on('prefix', (prefix, iri) => {
113
- // ignore empty (default) namespace
114
- if (prefix) {
115
- this.config.prefixes[prefix] = iri
116
- }
117
- })
118
- .on('end', () => {
119
- resolve(null)
120
- })
121
- parser.write(input)
122
- parser.end()
123
- } else {
124
- ttlParser.parse(input, (error: Error, quad: Quad, prefixes: Prefixes) => {
125
- if (error) {
126
- console.warn('failed parsing graph', graph, error.message)
127
- return reject(error)
128
- }
129
- if (quad) {
130
- addQuad(quad)
131
- return
132
- }
133
- if (prefixes) {
134
- this.config.registerPrefixes(prefixes)
135
- }
136
- resolve(null)
137
- })
138
- }
101
+ })
102
+ .on('error', (error) => {
103
+ console.warn('failed parsing graph', graph, error.message)
104
+ reject(error)
105
+ })
106
+ .on('prefix', (prefix, iri) => {
107
+ // ignore empty (default) namespace
108
+ if (prefix) {
109
+ this.config.prefixes[prefix] = iri
110
+ }
111
+ })
112
+ .on('end', () => {
113
+ resolve(null)
114
+ })
115
+ parser.write(input)
116
+ parser.end()
139
117
  })
140
118
  try {
141
119
  await Promise.allSettled(dependencies)
package/src/util.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Literal, NamedNode, Prefixes, Quad, Store } from 'n3'
2
- import { DATA_GRAPH, PREFIX_FOAF, PREFIX_RDFS, PREFIX_SHACL, PREFIX_SKOS, RDFS_PREDICATE_SUBCLASS_OF, RDF_PREDICATE_TYPE, SHAPES_GRAPH, SKOS_PREDICATE_BROADER, SKOS_PREDICATE_NARROWER } from './constants'
2
+ import { DATA_GRAPH, PREFIX_FOAF, PREFIX_RDF, PREFIX_RDFS, PREFIX_SHACL, PREFIX_SKOS, RDFS_PREDICATE_SUBCLASS_OF, RDF_PREDICATE_TYPE, SHAPES_GRAPH, SKOS_PREDICATE_BROADER, SKOS_PREDICATE_NARROWER } from './constants'
3
3
  import { Term } from '@rdfjs/types'
4
4
  import { InputListEntry } from './theme'
5
5
  import { ShaclPropertyTemplate } from './property-template'
@@ -44,7 +44,7 @@ export function findObjectByPredicate(quads: Quad[], predicate: string, prefix:
44
44
  }
45
45
 
46
46
  export function focusFirstInputElement(context: HTMLElement) {
47
- (context.querySelector('input,select,textarea') as HTMLElement)?.focus()
47
+ (context.querySelector('.editor') as HTMLElement)?.focus()
48
48
  }
49
49
 
50
50
  export function findLabel(quads: Quad[], languages: string[]): string {
@@ -170,4 +170,106 @@ export function prioritizeByLanguage(languages: string[], text1?: Literal, text2
170
170
  return text1
171
171
  }
172
172
  return index2 > index1 ? text1 : text2
173
- }
173
+ }
174
+
175
+ /*
176
+ This code is taken from https://github.com/rdfjs/N3.js/blob/main/src/N3Store.js and adapted to allow rdf:type triples in lists.
177
+ Can be removed as soon as https://github.com/rdfjs/N3.js/issues/546 is fixed.
178
+ */
179
+ export function extractLists(store: Store, { remove = false, ignoreErrors = false } = {}) {
180
+ const lists: Record<string, Term[]> = {} // has scalar keys so could be a simple Object
181
+ const onError = ignoreErrors ? (() => true) :
182
+ ((node: Term, message: string) => { throw new Error(`${node.value} ${message}`) })
183
+
184
+ // Traverse each list from its tail
185
+ const tails = store.getQuads(null, PREFIX_RDF + 'rest', PREFIX_RDF + 'nil', null)
186
+ const toRemove = remove ? [...tails] : []
187
+ tails.forEach(tailQuad => {
188
+ const items = [] // the members found as objects of rdf:first quads
189
+ let malformed = false // signals whether the current list is malformed
190
+ let head // the head of the list (_:b1 in above example)
191
+ let headPos: string // set to subject or object when head is set
192
+ const graph = tailQuad.graph // make sure list is in exactly one graph
193
+
194
+ // Traverse the list from tail to end
195
+ let current: Term | null = tailQuad.subject
196
+ while (current && !malformed) {
197
+ const objectQuads = store.getQuads(null, null, current, null)
198
+ const subjectQuads = store.getQuads(current, null, null, null).filter(quad => !quad.predicate.equals(RDF_PREDICATE_TYPE))
199
+ let quad, first = null, rest = null, parent = null
200
+
201
+ // Find the first and rest of this list node
202
+ for (let i = 0; i < subjectQuads.length && !malformed; i++) {
203
+ quad = subjectQuads[i]
204
+ if (!quad.graph.equals(graph))
205
+ malformed = onError(current, 'not confined to single graph')
206
+ else if (head)
207
+ malformed = onError(current, 'has non-list arcs out')
208
+
209
+ // one rdf:first
210
+ else if (quad.predicate.value === PREFIX_RDF + 'first') {
211
+ if (first)
212
+ malformed = onError(current, 'has multiple rdf:first arcs')
213
+ else
214
+ toRemove.push(first = quad)
215
+ }
216
+
217
+ // one rdf:rest
218
+ else if (quad.predicate.value === PREFIX_RDF + 'rest') {
219
+ if (rest)
220
+ malformed = onError(current, 'has multiple rdf:rest arcs')
221
+ else
222
+ toRemove.push(rest = quad)
223
+ }
224
+
225
+ // alien triple
226
+ else if (objectQuads.length)
227
+ malformed = onError(current, 'can\'t be subject and object')
228
+ else {
229
+ head = quad // e.g. { (1 2 3) :p :o }
230
+ headPos = 'subject'
231
+ }
232
+ }
233
+
234
+ // { :s :p (1 2) } arrives here with no head
235
+ // { (1 2) :p :o } arrives here with head set to the list.
236
+ for (let i = 0; i < objectQuads.length && !malformed; ++i) {
237
+ quad = objectQuads[i]
238
+ if (head)
239
+ malformed = onError(current, 'can\'t have coreferences')
240
+ // one rdf:rest
241
+ else if (quad.predicate.value === PREFIX_RDF + 'rest') {
242
+ if (parent)
243
+ malformed = onError(current, 'has incoming rdf:rest arcs')
244
+ else
245
+ parent = quad
246
+ }
247
+ else {
248
+ head = quad // e.g. { :s :p (1 2) }
249
+ headPos = 'object'
250
+ }
251
+ }
252
+
253
+ // Store the list item and continue with parent
254
+ if (!first)
255
+ malformed = onError(current, 'has no list head')
256
+ else
257
+ items.unshift(first.object)
258
+ current = parent && parent.subject
259
+ }
260
+
261
+ // Don't remove any quads if the list is malformed
262
+ if (malformed)
263
+ remove = false
264
+ // Store the list under the value of its head
265
+ else if (head) {
266
+ // @ts-ignore
267
+ lists[head[headPos].value] = items
268
+ }
269
+ })
270
+
271
+ // Remove list quads if requested
272
+ if (remove)
273
+ store.removeQuads(toRemove)
274
+ return lists
275
+ }