@ulb-darmstadt/shacl-form 1.10.3 → 2.0.0-rc1

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.
Files changed (55) hide show
  1. package/README.md +1 -1
  2. package/dist/bundle.js +412 -0
  3. package/dist/constants.d.ts +16 -16
  4. package/dist/constraints.d.ts +2 -2
  5. package/dist/form.d.ts +4 -3
  6. package/dist/index.js +62 -0
  7. package/dist/plugins/assets/plugin-VN3CfgGe.js +1 -0
  8. package/dist/plugins/file-upload.js +1 -0
  9. package/dist/plugins/leaflet.d.ts +3 -1
  10. package/dist/plugins/leaflet.js +5 -708
  11. package/dist/plugins/map-util.js +1 -0
  12. package/dist/{themes/default.d.ts → theme.default.d.ts} +2 -2
  13. package/package.json +28 -33
  14. package/dist/form-bootstrap.d.ts +0 -5
  15. package/dist/form-bootstrap.js +0 -413
  16. package/dist/form-default.d.ts +0 -5
  17. package/dist/form-default.js +0 -402
  18. package/dist/form-material.d.ts +0 -5
  19. package/dist/form-material.js +0 -722
  20. package/dist/plugins/fixed-list.d.ts +0 -9
  21. package/dist/plugins/mapbox.d.ts +0 -19
  22. package/dist/plugins/mapbox.js +0 -2870
  23. package/dist/themes/bootstrap.d.ts +0 -10
  24. package/dist/themes/material.d.ts +0 -15
  25. package/src/config.ts +0 -110
  26. package/src/constants.ts +0 -30
  27. package/src/constraints.ts +0 -149
  28. package/src/exports.ts +0 -7
  29. package/src/form-bootstrap.ts +0 -12
  30. package/src/form-default.ts +0 -12
  31. package/src/form-material.ts +0 -12
  32. package/src/form.ts +0 -319
  33. package/src/globals.d.ts +0 -2
  34. package/src/group.ts +0 -34
  35. package/src/loader.ts +0 -187
  36. package/src/node.ts +0 -192
  37. package/src/plugin.ts +0 -60
  38. package/src/plugins/file-upload.ts +0 -26
  39. package/src/plugins/fixed-list.ts +0 -19
  40. package/src/plugins/leaflet.ts +0 -196
  41. package/src/plugins/map-util.ts +0 -41
  42. package/src/plugins/mapbox.ts +0 -157
  43. package/src/property-template.ts +0 -151
  44. package/src/property.ts +0 -309
  45. package/src/serialize.ts +0 -96
  46. package/src/shacl-engine.d.ts +0 -2
  47. package/src/styles.css +0 -49
  48. package/src/theme.ts +0 -132
  49. package/src/themes/bootstrap.css +0 -6
  50. package/src/themes/bootstrap.ts +0 -44
  51. package/src/themes/default.css +0 -4
  52. package/src/themes/default.ts +0 -255
  53. package/src/themes/material.css +0 -14
  54. package/src/themes/material.ts +0 -250
  55. package/src/util.ts +0 -275
package/src/form.ts DELETED
@@ -1,319 +0,0 @@
1
- import { ShaclNode } from './node'
2
- import { Config } from './config'
3
- import { ClassInstanceProvider, Plugin, listPlugins, registerPlugin } from './plugin'
4
- import { Store, NamedNode, DataFactory, Quad, BlankNode } from 'n3'
5
- import { DATA_GRAPH, DCTERMS_PREDICATE_CONFORMS_TO, PREFIX_SHACL, RDF_PREDICATE_TYPE, SHACL_OBJECT_NODE_SHAPE, SHACL_PREDICATE_TARGET_CLASS, SHAPES_GRAPH } from './constants'
6
- import { Editor, Theme } from './theme'
7
- import { serialize } from './serialize'
8
- import { Validator } from 'shacl-engine'
9
- import { RokitCollapsible } from '@ro-kit/ui-widgets'
10
-
11
- export class ShaclForm extends HTMLElement {
12
- static get observedAttributes() { return Config.dataAttributes() }
13
-
14
- config: Config
15
- shape: ShaclNode | null = null
16
- form: HTMLFormElement
17
- initDebounceTimeout: ReturnType<typeof setTimeout> | undefined
18
-
19
- constructor(theme: Theme) {
20
- super()
21
-
22
- this.attachShadow({ mode: 'open' })
23
- this.form = document.createElement('form')
24
- this.config = new Config(theme, this.form)
25
- this.form.addEventListener('change', ev => {
26
- ev.stopPropagation()
27
- if (this.config.editMode) {
28
- this.validate(true).then(report => {
29
- this.dispatchEvent(new CustomEvent('change', { bubbles: true, cancelable: false, composed: true, detail: { 'valid': report.conforms, 'report': report } }))
30
- }).catch(e => { console.warn(e) })
31
- }
32
- })
33
- }
34
-
35
- connectedCallback() {
36
- this.shadowRoot!.prepend(this.form)
37
- }
38
-
39
- attributeChangedCallback() {
40
- this.config.updateAttributes(this)
41
- this.initialize()
42
- }
43
-
44
- private initialize() {
45
- clearTimeout(this.initDebounceTimeout)
46
- // set loading attribute on element so that hosting app can apply special css rules
47
- this.setAttribute('loading', '')
48
- // remove all child elements from form and show loading indicator
49
- this.form.replaceChildren(document.createTextNode(this.config.attributes.loading))
50
- this.initDebounceTimeout = setTimeout(async () => {
51
- try {
52
- await this.config.loader.loadGraphs()
53
- // remove loading indicator
54
- this.form.replaceChildren()
55
- // reset rendered node references
56
- this.config.renderedNodes.clear()
57
- // find root shacl shape
58
- const rootShapeShaclSubject = this.findRootShaclShapeSubject()
59
- if (rootShapeShaclSubject) {
60
- // remove all previous css classes to have a defined state
61
- this.form.classList.forEach(value => { this.form.classList.remove(value) })
62
- this.form.classList.toggle('mode-edit', this.config.editMode)
63
- this.form.classList.toggle('mode-view', !this.config.editMode)
64
- // let theme add classes to form element
65
- this.config.theme.apply(this.form)
66
- // adopt stylesheets from theme and plugins
67
- const styles: CSSStyleSheet[] = [ this.config.theme.stylesheet ]
68
- for (const plugin of listPlugins()) {
69
- if (plugin.stylesheet) {
70
- styles.push(plugin.stylesheet)
71
- }
72
- }
73
- this.shadowRoot!.adoptedStyleSheets = styles
74
-
75
- this.shape = new ShaclNode(rootShapeShaclSubject, this.config, this.config.attributes.valuesSubject ? DataFactory.namedNode(this.config.attributes.valuesSubject) : undefined)
76
- this.form.appendChild(this.shape)
77
-
78
- if (this.config.editMode) {
79
- // add submit button
80
- if (this.config.attributes.submitButton !== null) {
81
- const button = this.config.theme.createButton(this.config.attributes.submitButton || 'Submit', true)
82
- button.addEventListener('click', (event) => {
83
- event.preventDefault()
84
- // let browser check form validity first
85
- if (this.form.reportValidity()) {
86
- // now validate data graph
87
- this.validate().then(report => {
88
- if (report?.conforms) {
89
- // form and data graph are valid, so fire submit event
90
- this.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }))
91
- } else {
92
- // focus first invalid element
93
- let invalidEditor = this.form.querySelector(':scope .invalid > .editor')
94
- if (invalidEditor) {
95
- (invalidEditor as HTMLElement).focus()
96
- } else {
97
- this.form.querySelector(':scope .invalid')?.scrollIntoView()
98
- }
99
- }
100
- })
101
- }
102
- })
103
- this.form.appendChild(button)
104
- }
105
- // delete bound values from data graph, otherwise validation would be confused
106
- if (this.config.attributes.valuesSubject) {
107
- this.removeFromDataGraph(DataFactory.namedNode(this.config.attributes.valuesSubject))
108
- }
109
- await this.validate(true)
110
- }
111
- } else if (this.config.store.countQuads(null, null, null, SHAPES_GRAPH) > 0) {
112
- // raise error only when shapes graph is not empty
113
- throw new Error('shacl root node shape not found')
114
- }
115
- } catch (e) {
116
- console.error(e)
117
- const errorDisplay = document.createElement('div')
118
- errorDisplay.innerText = String(e)
119
- this.form.replaceChildren(errorDisplay)
120
- }
121
- this.removeAttribute('loading')
122
- }, 200)
123
- }
124
-
125
- public serialize(format = 'text/turtle', graph = this.toRDF()): string {
126
- const quads = graph.getQuads(null, null, null, null)
127
- return serialize(quads, format, this.config.prefixes)
128
- }
129
-
130
- public toRDF(graph = new Store()): Store {
131
- this.shape?.toRDF(graph)
132
- return graph
133
- }
134
-
135
- public registerPlugin(plugin: Plugin) {
136
- registerPlugin(plugin)
137
- this.initialize()
138
- }
139
-
140
- public setTheme(theme: Theme) {
141
- this.config.theme = theme
142
- this.initialize()
143
- }
144
-
145
- public setClassInstanceProvider(provider: ClassInstanceProvider) {
146
- this.config.classInstanceProvider = provider
147
- this.initialize()
148
- }
149
-
150
- /* Returns the validation report */
151
- public async validate(ignoreEmptyValues = false): Promise<any> {
152
- for (const elem of this.form.querySelectorAll(':scope .validation-error')) {
153
- elem.remove()
154
- }
155
- for (const elem of this.form.querySelectorAll(':scope .property-instance')) {
156
- elem.classList.remove('invalid')
157
- if (((elem.querySelector(':scope > .editor')) as Editor)?.value) {
158
- elem.classList.add('valid')
159
- } else {
160
- elem.classList.remove('valid')
161
- }
162
- }
163
-
164
- this.config.store.deleteGraph(this.config.valuesGraphId || '')
165
- if (this.shape) {
166
- this.shape.toRDF(this.config.store)
167
- // add node target for validation. this is required in case of missing sh:targetClass in root shape
168
- this.config.store.add(new Quad(this.shape.shaclSubject, DataFactory.namedNode(PREFIX_SHACL + 'targetNode'), this.shape.nodeId, this.config.valuesGraphId))
169
- }
170
- try {
171
- const dataset = this.config.store
172
- const report = await new Validator(dataset, { details: true, factory: DataFactory }).validate({ dataset })
173
-
174
- for (const result of report.results) {
175
- if (result.focusNode?.ptrs?.length) {
176
- for (const ptr of result.focusNode.ptrs) {
177
- const focusNode = ptr._term
178
- // result.path can be empty, e.g. if a focus node does not contain a required property node
179
- if (result.path?.length) {
180
- const path = result.path[0].predicates[0]
181
- // try to find most specific editor elements first
182
- let invalidElements = this.form.querySelectorAll(`
183
- :scope shacl-node[data-node-id='${focusNode.id}'] > shacl-property > .property-instance[data-path='${path.id}'] > .editor,
184
- :scope shacl-node[data-node-id='${focusNode.id}'] > shacl-property > .shacl-group > .property-instance[data-path='${path.id}'] > .editor,
185
- :scope shacl-node[data-node-id='${focusNode.id}'] > .shacl-group > shacl-property > .property-instance[data-path='${path.id}'] > .editor,
186
- :scope shacl-node[data-node-id='${focusNode.id}'] > .shacl-group > shacl-property > .shacl-group > .property-instance[data-path='${path.id}'] > .editor`)
187
- if (invalidElements.length === 0) {
188
- // if no editors found, select respective node. this will be the case for node shape violations.
189
- invalidElements = this.form.querySelectorAll(`
190
- :scope [data-node-id='${focusNode.id}'] > shacl-property > .property-instance[data-path='${path.id}'],
191
- :scope [data-node-id='${focusNode.id}'] > shacl-property > .shacl-group > .property-instance[data-path='${path.id}']`)
192
- }
193
-
194
- for (const invalidElement of invalidElements) {
195
- if (invalidElement.classList.contains('editor')) {
196
- // this is a property shape violation
197
- if (!ignoreEmptyValues || (invalidElement as Editor).value) {
198
- let parent: HTMLElement | null = invalidElement.parentElement!
199
- parent.classList.add('invalid')
200
- parent.classList.remove('valid')
201
- parent.appendChild(this.createValidationErrorDisplay(result))
202
- do {
203
- if (parent instanceof RokitCollapsible) {
204
- parent.open = true
205
- }
206
- parent = parent.parentElement
207
- } while (parent)
208
- }
209
- } else if (!ignoreEmptyValues) {
210
- // this is a node shape violation
211
- invalidElement.classList.add('invalid')
212
- invalidElement.classList.remove('valid')
213
- invalidElement.appendChild(this.createValidationErrorDisplay(result, 'node'))
214
- }
215
- }
216
- } else if (!ignoreEmptyValues) {
217
- this.form.querySelector(`:scope [data-node-id='${focusNode.id}']`)?.prepend(this.createValidationErrorDisplay(result, 'node'))
218
- }
219
- }
220
- }
221
- }
222
- return report
223
- } catch(e) {
224
- console.error(e)
225
- return false
226
- }
227
- }
228
-
229
- private createValidationErrorDisplay(validatonResult?: any, clazz?: string): HTMLElement {
230
- const messageElement = document.createElement('span')
231
- messageElement.classList.add('validation-error')
232
- if (clazz) {
233
- messageElement.classList.add(clazz)
234
- }
235
- if (validatonResult) {
236
- if (validatonResult.message?.length > 0) {
237
- for (const message of validatonResult.message) {
238
- messageElement.title += message.value + '\n'
239
- }
240
- } else {
241
- messageElement.title = validatonResult.sourceConstraintComponent?.value
242
- }
243
- }
244
- return messageElement
245
- }
246
-
247
- private findRootShaclShapeSubject(): NamedNode | undefined {
248
- let rootShapeShaclSubject: NamedNode | null = null
249
- // if data-shape-subject is set, use that
250
- if (this.config.attributes.shapeSubject) {
251
- rootShapeShaclSubject = DataFactory.namedNode(this.config.attributes.shapeSubject)
252
- if (this.config.store.getQuads(rootShapeShaclSubject, RDF_PREDICATE_TYPE, SHACL_OBJECT_NODE_SHAPE, null).length === 0) {
253
- console.warn(`shapes graph does not contain requested root shape ${this.config.attributes.shapeSubject}`)
254
- return
255
- }
256
- }
257
- else {
258
- // if we have a data graph and data-values-subject is set, use shape of that
259
- if (this.config.attributes.valuesSubject && this.config.store.countQuads(null, null, null, DATA_GRAPH) > 0) {
260
- const rootValueSubject = DataFactory.namedNode(this.config.attributes.valuesSubject)
261
- const rootValueSubjectTypes = [
262
- ...this.config.store.getQuads(rootValueSubject, RDF_PREDICATE_TYPE, null, DATA_GRAPH),
263
- ...this.config.store.getQuads(rootValueSubject, DCTERMS_PREDICATE_CONFORMS_TO, null, DATA_GRAPH)
264
- ]
265
- if (rootValueSubjectTypes.length === 0) {
266
- console.warn(`value subject '${this.config.attributes.valuesSubject}' has neither ${RDF_PREDICATE_TYPE.id} nor ${DCTERMS_PREDICATE_CONFORMS_TO.id} statement`)
267
- return
268
- }
269
- // if type/conformsTo refers to a node shape, prioritize that over targetClass resolution
270
- for (const rootValueSubjectType of rootValueSubjectTypes) {
271
- if (this.config.store.getQuads(rootValueSubjectType.object as NamedNode, RDF_PREDICATE_TYPE, SHACL_OBJECT_NODE_SHAPE, null).length > 0) {
272
- rootShapeShaclSubject = rootValueSubjectType.object as NamedNode
273
- break
274
- }
275
- }
276
- if (!rootShapeShaclSubject) {
277
- const rootShapes = this.config.store.getQuads(null, SHACL_PREDICATE_TARGET_CLASS, rootValueSubjectTypes[0].object, null)
278
- if (rootShapes.length === 0) {
279
- console.error(`value subject '${this.config.attributes.valuesSubject}' has no shacl shape definition in the shapes graph`)
280
- return
281
- }
282
- if (rootShapes.length > 1) {
283
- console.warn(`value subject '${this.config.attributes.valuesSubject}' has multiple shacl shape definitions in the shapes graph, choosing the first found (${rootShapes[0].subject})`)
284
- }
285
- if (this.config.store.getQuads(rootShapes[0].subject, RDF_PREDICATE_TYPE, SHACL_OBJECT_NODE_SHAPE, null).length === 0) {
286
- console.error(`value subject '${this.config.attributes.valuesSubject}' references a shape which is not a NodeShape (${rootShapes[0].subject})`)
287
- return
288
- }
289
- rootShapeShaclSubject = rootShapes[0].subject as NamedNode
290
- }
291
- }
292
- else {
293
- // choose first of all defined root shapes
294
- const rootShapes = this.config.store.getQuads(null, RDF_PREDICATE_TYPE, SHACL_OBJECT_NODE_SHAPE, null)
295
- if (rootShapes.length == 0) {
296
- console.warn('shapes graph does not contain any root shapes')
297
- return
298
- }
299
- if (rootShapes.length > 1) {
300
- console.warn('shapes graph contains', rootShapes.length, 'root shapes. choosing first found which is', rootShapes[0].subject.value)
301
- console.info('hint: set the shape to use with attribute "data-shape-subject"')
302
- }
303
- rootShapeShaclSubject = rootShapes[0].subject as NamedNode
304
- }
305
- }
306
- return rootShapeShaclSubject
307
- }
308
-
309
- private removeFromDataGraph(subject: NamedNode | BlankNode) {
310
- this.config.attributes.valuesSubject
311
- for (const quad of this.config.store.getQuads(subject, null, null, DATA_GRAPH)) {
312
- this.config.store.delete(quad)
313
- if (quad.object.termType === 'NamedNode' || quad.object.termType === 'BlankNode') {
314
- // recurse
315
- this.removeFromDataGraph(quad.object)
316
- }
317
- }
318
- }
319
- }
package/src/globals.d.ts DELETED
@@ -1,2 +0,0 @@
1
- declare module "*.css";
2
- declare module "*.css?raw";
package/src/group.ts DELETED
@@ -1,34 +0,0 @@
1
- import { PREFIX_RDFS } from './constants'
2
- import { Config } from './config'
3
- import { findObjectValueByPredicate } from './util'
4
- import { RokitCollapsible } from '@ro-kit/ui-widgets'
5
-
6
- export function createShaclGroup(groupSubject: string, config: Config): HTMLElement {
7
- let name = groupSubject
8
- const quads = config.store.getQuads(groupSubject, null, null, null)
9
- const label = findObjectValueByPredicate(quads, "label", PREFIX_RDFS, config.languages)
10
- if (label) {
11
- name = label
12
- }
13
-
14
- let group: HTMLElement
15
- if (config.attributes.collapse !== null) {
16
- group = new RokitCollapsible()
17
- group.classList.add('collapsible');
18
- (group as RokitCollapsible).open = config.attributes.collapse === 'open';
19
- (group as RokitCollapsible).label = name
20
- } else {
21
- group = document.createElement('div')
22
- const header = document.createElement('h1')
23
- header.innerText = name
24
- group.appendChild(header)
25
- }
26
-
27
- group.dataset['subject'] = groupSubject
28
- group.classList.add('shacl-group')
29
- const order = findObjectValueByPredicate(quads, "order")
30
- if (order) {
31
- group.style.order = order
32
- }
33
- return group
34
- }
package/src/loader.ts DELETED
@@ -1,187 +0,0 @@
1
- import { Store, Quad, NamedNode, DataFactory, StreamParser } from 'n3'
2
- import { DATA_GRAPH, DCTERMS_PREDICATE_CONFORMS_TO, OWL_PREDICATE_IMPORTS, SHACL_PREDICATE_CLASS, SHACL_PREDICATE_TARGET_CLASS, SHAPES_GRAPH } from './constants'
3
- import { Config } from './config'
4
- import { isURL } from './util'
5
- import { RdfXmlParser } from 'rdfxml-streaming-parser'
6
- import { toRDF } from 'jsonld'
7
-
8
-
9
- // cache external data in module scope (and not in Loader instance) to avoid requesting
10
- // them multiple times, e.g. when more than one shacl-form element is on the page
11
- // that import the same resources
12
- const loadedURLCache: Record<string, Promise<string>> = {}
13
- const loadedClassesCache: Record<string, Promise<string>> = {}
14
-
15
- export class Loader {
16
- private config: Config
17
- private loadedExternalUrls: string[] = []
18
- private loadedClasses: string[] = []
19
-
20
- constructor(config: Config) {
21
- this.config = config
22
- }
23
-
24
- async loadGraphs() {
25
- // clear local caches
26
- this.loadedExternalUrls = []
27
- this.loadedClasses = []
28
- this.config.prefixes = {}
29
-
30
-
31
- const promises: Promise<void>[] = []
32
- const store = new Store()
33
- 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))
34
- 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))
35
- await Promise.all(promises)
36
-
37
- // if shapes graph is empty, but we have the following triples:
38
- // <valueSubject> a <uri> or <valueSubject> dcterms:conformsTo <uri>
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 && this.config.attributes.valuesSubject) {
42
- const shapeCandidates = [
43
- // ...store.getObjects(this.config.attributes.valuesSubject, RDF_PREDICATE_TYPE, DATA_GRAPH),
44
- ...store.getObjects(this.config.attributes.valuesSubject, DCTERMS_PREDICATE_CONFORMS_TO, DATA_GRAPH)
45
- ]
46
- const promises: Promise<void>[] = []
47
- for (const uri of shapeCandidates) {
48
- const url = this.toURL(uri.value)
49
- if (url && this.loadedExternalUrls.indexOf(url) < 0) {
50
- this.loadedExternalUrls.push(url)
51
- promises.push(this.importRDF(this.fetchRDF(url), store, SHAPES_GRAPH))
52
- }
53
- }
54
- try {
55
- await Promise.allSettled(promises)
56
- } catch (e) {
57
- console.warn(e)
58
- }
59
- }
60
-
61
- this.config.store = store
62
- }
63
-
64
- async importRDF(input: string | Promise<string>, store: Store, graph?: NamedNode) {
65
- const parse = async (input: string) => {
66
- const dependencies: Promise<void>[] = []
67
- await new Promise((resolve, reject) => {
68
- const parser = guessContentType(input) === 'xml' ? new RdfXmlParser() : new StreamParser()
69
- parser.on('data', (quad: Quad) => {
70
- store.add(new Quad(quad.subject, quad.predicate, quad.object, graph))
71
- // check if this is an owl:imports predicate and try to load the url
72
- if (this.config.attributes.ignoreOwlImports === null && OWL_PREDICATE_IMPORTS.equals(quad.predicate)) {
73
- const url = this.toURL(quad.object.value)
74
- // import url only once
75
- if (url && this.loadedExternalUrls.indexOf(url) < 0) {
76
- this.loadedExternalUrls.push(url)
77
- // import into separate graph
78
- dependencies.push(this.importRDF(this.fetchRDF(url), store, DataFactory.namedNode(url)))
79
- }
80
- }
81
- // check if this is an sh:class predicate and invoke class instance provider
82
- if (this.config.classInstanceProvider && (SHACL_PREDICATE_CLASS.equals(quad.predicate) || SHACL_PREDICATE_TARGET_CLASS.equals(quad.predicate))) {
83
- const className = quad.object.value
84
- // import class definitions only once
85
- if (this.loadedClasses.indexOf(className) < 0) {
86
- let promise: Promise<string>
87
- // check if class is in module scope cache
88
- if (className in loadedClassesCache) {
89
- promise = loadedClassesCache[className]
90
- } else {
91
- promise = this.config.classInstanceProvider(className)
92
- loadedClassesCache[className] = promise
93
- }
94
- this.loadedClasses.push(className)
95
- dependencies.push(this.importRDF(promise, store, graph))
96
- }
97
- }
98
- })
99
- .on('error', (error) => {
100
- console.warn('failed parsing graph', graph, error.message)
101
- reject(error)
102
- })
103
- .on('prefix', (prefix, iri) => {
104
- // ignore empty (default) namespace
105
- if (prefix) {
106
- this.config.prefixes[prefix] = iri
107
- }
108
- })
109
- .on('end', () => {
110
- resolve(null)
111
- })
112
- parser.write(input)
113
- parser.end()
114
- })
115
- try {
116
- await Promise.allSettled(dependencies)
117
- } catch (e) {
118
- console.warn(e)
119
- }
120
- }
121
-
122
- if (input instanceof Promise) {
123
- input = await input
124
- }
125
- if (input) {
126
- if (guessContentType(input) === 'json') {
127
- // convert json to n-quads
128
- try {
129
- input = await toRDF(JSON.parse(input), { format: 'application/n-quads' }) as string
130
- } catch(e) {
131
- console.error(e)
132
- }
133
- }
134
- await parse(input)
135
- }
136
- }
137
-
138
- toURL(id: string): string | null {
139
- if (isURL(id)) {
140
- return id
141
- }
142
- if (this.config.prefixes) {
143
- const splitted = id.split(':')
144
- if (splitted.length === 2) {
145
- const prefix = this.config.prefixes[splitted[0]]
146
- if (prefix) {
147
- // need to ignore type check. 'prefix' is a string and not a NamedNode<string> (seems to be a bug in n3 typings)
148
- // @ts-ignore
149
- id = id.replace(`${splitted[0]}:`, prefix)
150
- if (isURL(id)) {
151
- return id
152
- }
153
- }
154
- }
155
- }
156
- return null
157
- }
158
-
159
- async fetchRDF(url: string): Promise<string> {
160
- // try to load from cache first
161
- if (url in loadedURLCache) {
162
- return loadedURLCache[url]
163
- }
164
- let proxiedURL = url
165
- // if we have a proxy configured, then load url via proxy
166
- if (this.config.attributes.proxy) {
167
- proxiedURL = this.config.attributes.proxy + encodeURIComponent(url)
168
- }
169
- const promise = fetch(proxiedURL, {
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
- }
177
- }
178
-
179
- /* Can't rely on HTTP content-type header, since many resources are delivered with text/plain */
180
- function guessContentType(input: string) {
181
- if (/^\s*\{/.test(input)) {
182
- return 'json'
183
- } else if (/^\s*<\?xml/.test(input)) {
184
- return 'xml'
185
- }
186
- return 'ttl'
187
- }