rdflib 2.2.31 → 2.2.32-2f2a2f3c

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 (54) hide show
  1. package/dist/670.rdflib.min.js +1 -0
  2. package/dist/730.rdflib.min.js +3 -0
  3. package/dist/730.rdflib.min.js.LICENSE.txt +58 -0
  4. package/dist/730.rdflib.min.js.map +1 -0
  5. package/dist/rdflib.min.js +1 -1
  6. package/dist/rdflib.min.js.LICENSE.txt +0 -59
  7. package/dist/rdflib.min.js.map +1 -1
  8. package/esm/blank-node.js +10 -6
  9. package/esm/collection.js +3 -4
  10. package/esm/factories/factory-types.js +10 -10
  11. package/esm/fetcher.js +64 -35
  12. package/esm/formula.js +10 -13
  13. package/esm/jsonldparser.js +4 -3
  14. package/esm/lists.js +2 -1
  15. package/esm/literal.js +6 -8
  16. package/esm/node-internal.js +5 -10
  17. package/esm/rdfxmlparser.js +3 -0
  18. package/esm/serializer.js +2 -3
  19. package/esm/statement.js +7 -11
  20. package/esm/store.js +39 -32
  21. package/esm/types.js +18 -1
  22. package/esm/update-manager.js +150 -83
  23. package/esm/utils.js +0 -1
  24. package/esm/variable.js +2 -4
  25. package/lib/blank-node.js +10 -6
  26. package/lib/collection.js +3 -4
  27. package/lib/factories/factory-types.js +10 -10
  28. package/lib/fetcher.js +88 -36
  29. package/lib/formula.js +10 -13
  30. package/lib/index.d.ts +1 -1
  31. package/lib/jsonldparser.js +9 -3
  32. package/lib/lists.js +15 -1
  33. package/lib/literal.js +6 -8
  34. package/lib/node-internal.js +5 -10
  35. package/lib/query.d.ts +1 -1
  36. package/lib/rdfxmlparser.js +3 -0
  37. package/lib/serializer.d.ts +1 -1
  38. package/lib/serializer.js +2 -3
  39. package/lib/sparql-to-query.d.ts +1 -1
  40. package/lib/statement.js +7 -11
  41. package/lib/store.d.ts +1 -1
  42. package/lib/store.js +55 -34
  43. package/lib/types.js +22 -0
  44. package/lib/update-manager.d.ts +25 -5
  45. package/lib/update-manager.js +154 -82
  46. package/lib/utils-js.d.ts +3 -3
  47. package/lib/variable.js +2 -4
  48. package/lib/xsd-internal.d.ts +1 -1
  49. package/package.json +20 -19
  50. package/src/fetcher.ts +13 -0
  51. package/src/jsonldparser.js +2 -4
  52. package/src/serializer.js +1 -1
  53. package/src/store.ts +18 -1
  54. package/src/update-manager.ts +243 -163
@@ -1,20 +1,20 @@
1
1
  /* @file Update Manager Class
2
2
  **
3
- ** 2007-07-15 originall sparl update module by Joe Presbrey <presbrey@mit.edu>
3
+ ** 2007-07-15 original SPARQL Update module by Joe Presbrey <presbrey@mit.edu>
4
4
  ** 2010-08-08 TimBL folded in Kenny's WEBDAV
5
- ** 2010-12-07 TimBL addred local file write code
5
+ ** 2010-12-07 TimBL added local file write code
6
6
  */
7
7
  import IndexedFormula from './store'
8
- import {docpart, join as uriJoin} from './uri'
9
- import Fetcher, {Options} from './fetcher'
8
+ import { docpart, join as uriJoin } from './uri'
9
+ import Fetcher, { Options } from './fetcher'
10
10
  import Namespace from './namespace'
11
11
  import Serializer from './serializer'
12
- import {isBlankNode, isStore} from './utils/terms'
12
+ import { isBlankNode, isStore } from './utils/terms'
13
13
  import * as Util from './utils-js'
14
14
  import Statement from './statement'
15
15
  import RDFlibNamedNode from './named-node'
16
- import {termValue} from './utils/termValue'
17
- import {BlankNode, NamedNode, Quad, Quad_Graph, Quad_Object, Quad_Predicate, Quad_Subject, Term,} from './tf-types'
16
+ import { termValue } from './utils/termValue'
17
+ import { BlankNode, NamedNode, Quad, Quad_Graph, Quad_Object, Quad_Predicate, Quad_Subject, Term, } from './tf-types'
18
18
 
19
19
  interface UpdateManagerFormula extends IndexedFormula {
20
20
  fetcher: Fetcher
@@ -45,7 +45,7 @@ export default class UpdateManager {
45
45
  /**
46
46
  * @param store - The quadstore to store data and metadata. Created if not passed.
47
47
  */
48
- constructor (store?: IndexedFormula) {
48
+ constructor(store?: IndexedFormula) {
49
49
  store = store || new IndexedFormula()
50
50
  if (store.updater) {
51
51
  throw new Error("You can't have two UpdateManagers for the same store")
@@ -70,56 +70,65 @@ export default class UpdateManager {
70
70
  this.patchControl = []
71
71
  }
72
72
 
73
- patchControlFor (doc: NamedNode) {
73
+ patchControlFor(doc: NamedNode) {
74
74
  if (!this.patchControl[doc.value]) {
75
75
  this.patchControl[doc.value] = []
76
76
  }
77
77
  return this.patchControl[doc.value]
78
78
  }
79
79
 
80
- isHttpUri(uri:string){
81
- return( uri.slice(0,4) === 'http' )
80
+ isHttpUri(uri: string) {
81
+ return (uri.slice(0, 4) === 'http')
82
82
  }
83
83
 
84
- /** Remove from the store HTTP authorization metadata
85
- * The editble function below relies on copies we have in the store
86
- * of the results of previous HTTP transactions. Howver, when
87
- * the user logs in, then that data misrepresents what would happen
88
- * if the user tried again.
89
- */
90
- flagAuthorizationMetadata () {
91
- const kb = this.store
92
- const meta = kb.fetcher.appNode
93
- const requests = kb.statementsMatching(undefined, this.ns.link('requestedURI'), undefined, meta).map(st => st.subject)
94
- for (const request of requests) {
95
- const response = kb.any(request, this.ns.link('response'), null, meta) as Quad_Subject
96
- if (response !== undefined) { // ts
97
- this.store.add(response, this.ns.link('outOfDate'), true as any, meta) // @@ Boolean is fine - fix types
84
+ /** Remove from the store HTTP authorization metadata
85
+ * The editable function below relies on copies we have in the store
86
+ * of the results of previous HTTP transactions. However, when
87
+ * the user logs in, then that data misrepresents what would happen
88
+ * if the user tried again.
89
+ */
90
+ flagAuthorizationMetadata(kb?: IndexedFormula) {
91
+ if (!kb) {
92
+ kb = this.store
93
+ }
94
+ const meta = kb.fetcher?.appNode
95
+ const requests = kb.statementsMatching(undefined, this.ns.link('requestedURI'), undefined, meta).map(st => st.subject)
96
+ for (const request of requests) {
97
+ const response = kb.any(request, this.ns.link('response'), null, meta) as Quad_Subject
98
+ if (response !== undefined) { // ts
99
+ kb.add(response, this.ns.link('outOfDate'), true as any, meta) // @@ Boolean is fine - fix types
100
+ }
98
101
  }
99
102
  }
100
- }
101
103
 
102
- /**
103
- * Tests whether a file is editable.
104
- * If the file has a specific annotation that it is machine written,
105
- * for safety, it is editable (this doesn't actually check for write access)
106
- * If the file has wac-allow and accept patch headers, those are respected.
107
- * and local write access is determined by those headers.
108
- * This async version not only looks at past HTTP requests, it also makes new ones if necessary.
109
- *
110
- * @returns The method string SPARQL or DAV or
111
- * LOCALFILE or false if known, undefined if not known.
112
- */
113
- async checkEditable (uri: string | NamedNode, kb?: IndexedFormula): Promise<string | boolean | undefined> {
114
- const initial = this.editable(uri, kb)
115
- if (initial !== undefined) {
116
- return initial
117
- }
118
- await this.store.fetcher.load(uri)
119
- const final = this.editable(uri, kb)
120
- // console.log(`Loaded ${uri} just to check editable, result: ${final}.`)
121
- return final
122
- }
104
+ /**
105
+ * Tests whether a file is editable.
106
+ * If the file has a specific annotation that it is machine written,
107
+ * for safety, it is editable (this doesn't actually check for write access)
108
+ * If the file has wac-allow and accept patch headers, those are respected.
109
+ * and local write access is determined by those headers.
110
+ * This async version not only looks at past HTTP requests, it also makes new ones if necessary.
111
+ *
112
+ * @returns The method string N3PATCH or SPARQL or DAV or
113
+ * LOCALFILE or false if known, undefined if not known.
114
+ */
115
+ async checkEditable(uri: string | NamedNode, kb?: IndexedFormula): Promise<string | boolean | undefined> {
116
+ if (!uri) {
117
+ return false // Eg subject is bnode, no known doc to write to
118
+ }
119
+ if (!kb) {
120
+ kb = this.store
121
+ }
122
+
123
+ const initial = this.editable(uri, kb)
124
+ if (initial !== undefined) {
125
+ return initial
126
+ }
127
+ await kb.fetcher?.load(uri)
128
+ const final = this.editable(uri, kb)
129
+ // console.log(`Loaded ${uri} just to check editable, result: ${final}.`)
130
+ return final
131
+ }
123
132
  /**
124
133
  * Tests whether a file is editable.
125
134
  * If the file has a specific annotation that it is machine written,
@@ -131,7 +140,7 @@ flagAuthorizationMetadata () {
131
140
  * @returns The method string SPARQL or DAV or
132
141
  * LOCALFILE or false if known, undefined if not known.
133
142
  */
134
- editable (uri: string | NamedNode, kb?: IndexedFormula): string | boolean | undefined {
143
+ editable(uri: string | NamedNode, kb?: IndexedFormula): string | boolean | undefined {
135
144
  if (!uri) {
136
145
  return false // Eg subject is bnode, no known doc to write to
137
146
  }
@@ -140,21 +149,21 @@ flagAuthorizationMetadata () {
140
149
  }
141
150
  uri = termValue(uri)
142
151
 
143
- if ( !this.isHttpUri(uri as string) ) {
144
- if (this.store.holds(
145
- this.store.rdfFactory.namedNode(uri),
146
- this.store.rdfFactory.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
147
- this.store.rdfFactory.namedNode('http://www.w3.org/2007/ont/link#MachineEditableDocument'))) {
152
+ if (!this.isHttpUri(uri as string)) {
153
+ if (kb.holds(
154
+ kb.rdfFactory.namedNode(uri),
155
+ kb.rdfFactory.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
156
+ kb.rdfFactory.namedNode('http://www.w3.org/2007/ont/link#MachineEditableDocument'))) {
148
157
  return 'LOCALFILE'
149
158
  }
150
159
  }
151
160
 
152
161
  var request
153
162
  var definitive = false
154
- const meta = this.store.fetcher.appNode
163
+ const meta = kb.fetcher?.appNode
155
164
  // const kb = s
156
165
 
157
- // @ts-ignore passes a string to kb.each, which expects a term. Should this work?
166
+ // @ts-ignore passes a string to kb.each, which expects a term. Should this work?
158
167
  var requests = kb.each(undefined, this.ns.link('requestedURI'), docpart(uri), meta)
159
168
  var method: string
160
169
  for (var r = 0; r < requests.length; r++) {
@@ -170,7 +179,7 @@ flagAuthorizationMetadata () {
170
179
  if (wacAllow) {
171
180
  for (var bit of wacAllow.split(',')) {
172
181
  var lr = bit.split('=')
173
- if (lr[0].includes('user') && !lr[1].includes('write') && !lr[1].includes('append') ) {
182
+ if (lr[0].includes('user') && !lr[1].includes('write') && !lr[1].includes('append')) {
174
183
  // console.log(' editable? excluded by WAC-Allow: ', wacAllow)
175
184
  return false
176
185
  }
@@ -180,6 +189,7 @@ flagAuthorizationMetadata () {
180
189
  if (acceptPatch.length) {
181
190
  for (let i = 0; i < acceptPatch.length; i++) {
182
191
  method = acceptPatch[i].value.trim()
192
+ if (method.indexOf('text/n3') >= 0) return 'N3PATCH'
183
193
  if (method.indexOf('application/sparql-update') >= 0) return 'SPARQL'
184
194
  if (method.indexOf('application/sparql-update-single-match') >= 0) return 'SPARQL'
185
195
  }
@@ -197,8 +207,8 @@ flagAuthorizationMetadata () {
197
207
  }
198
208
  }
199
209
 
200
- if ( !this.isHttpUri(uri as string) ) {
201
- if( !wacAllow ) return false;
210
+ if (!this.isHttpUri(uri as string)) {
211
+ if (!wacAllow) return false;
202
212
  else return 'LOCALFILE';
203
213
  }
204
214
 
@@ -228,13 +238,15 @@ flagAuthorizationMetadata () {
228
238
  return undefined // We don't know (yet) as we haven't had a response (yet)
229
239
  }
230
240
 
231
- anonymize (obj) {
232
- return (obj.toNT().substr(0, 2) === '_:' && this.mentioned(obj))
241
+ anonymize(obj) {
242
+ let anonymized = (obj.toNT().substr(0, 2) === '_:' && this.mentioned(obj))
233
243
  ? '?' + obj.toNT().substr(2)
234
- : obj.toNT()
244
+ : obj.toNT();
245
+
246
+ return anonymized;
235
247
  }
236
248
 
237
- anonymizeNT (stmt: Quad) {
249
+ anonymizeNT(stmt: Quad) {
238
250
  return this.anonymize(stmt.subject) + ' ' +
239
251
  this.anonymize(stmt.predicate) + ' ' +
240
252
  this.anonymize(stmt.object) + ' .'
@@ -248,7 +260,7 @@ flagAuthorizationMetadata () {
248
260
  * Returns a list of all bnodes occurring in a statement
249
261
  * @private
250
262
  */
251
- statementBnodes (st: Quad): BlankNode[] {
263
+ statementBnodes(st: Quad): BlankNode[] {
252
264
  return [st.subject, st.predicate, st.object].filter(function (x) {
253
265
  return isBlankNode(x)
254
266
  }) as BlankNode[]
@@ -258,7 +270,7 @@ flagAuthorizationMetadata () {
258
270
  * Returns a list of all bnodes occurring in a list of statements
259
271
  * @private
260
272
  */
261
- statementArrayBnodes (sts: ReadonlyArray<Quad>) {
273
+ statementArrayBnodes(sts: ReadonlyArray<Quad>) {
262
274
  var bnodes: BlankNode[] = []
263
275
  for (let i = 0; i < sts.length; i++) {
264
276
  bnodes = bnodes.concat(this.statementBnodes(sts[i]))
@@ -277,7 +289,7 @@ flagAuthorizationMetadata () {
277
289
  * Makes a cached list of [Inverse-]Functional properties
278
290
  * @private
279
291
  */
280
- cacheIfps () {
292
+ cacheIfps() {
281
293
  this.ifps = {}
282
294
  var a = this.store.each(undefined, this.ns.rdf('type'),
283
295
  this.ns.owl('InverseFunctionalProperty'))
@@ -295,7 +307,7 @@ flagAuthorizationMetadata () {
295
307
  * Returns a context to bind a given node, up to a given depth
296
308
  * @private
297
309
  */
298
- bnodeContext2 (x, source, depth) {
310
+ bnodeContext2(x, source, depth) {
299
311
  // Return a list of statements which indirectly identify a node
300
312
  // Depth > 1 if try further indirection.
301
313
  // Return array of statements (possibly empty), or null if failure
@@ -306,12 +318,12 @@ flagAuthorizationMetadata () {
306
318
  if (this.fps[sts[i].predicate.value]) {
307
319
  y = sts[i].subject
308
320
  if (!y.isBlank) {
309
- return [ sts[i] ]
321
+ return [sts[i]]
310
322
  }
311
323
  if (depth) {
312
324
  res = this.bnodeContext2(y, source, depth - 1)
313
325
  if (res) {
314
- return res.concat([ sts[i] ])
326
+ return res.concat([sts[i]])
315
327
  }
316
328
  }
317
329
  }
@@ -322,12 +334,12 @@ flagAuthorizationMetadata () {
322
334
  if (this.ifps[sts[i].predicate.value]) {
323
335
  y = sts[i].object
324
336
  if (!y.isBlank) {
325
- return [ sts[i] ]
337
+ return [sts[i]]
326
338
  }
327
339
  if (depth) {
328
340
  res = this.bnodeContext2(y, source, depth - 1)
329
341
  if (res) {
330
- return res.concat([ sts[i] ])
342
+ return res.concat([sts[i]])
331
343
  }
332
344
  }
333
345
  }
@@ -339,7 +351,7 @@ flagAuthorizationMetadata () {
339
351
  * Returns the smallest context to bind a given single bnode
340
352
  * @private
341
353
  */
342
- bnodeContext1 (x, source) {
354
+ bnodeContext1(x, source) {
343
355
  // Return a list of statements which indirectly identify a node
344
356
  // Breadth-first
345
357
  for (var depth = 0; depth < 3; depth++) { // Try simple first
@@ -354,7 +366,7 @@ flagAuthorizationMetadata () {
354
366
  /**
355
367
  * @private
356
368
  */
357
- mentioned (x) {
369
+ mentioned(x) {
358
370
  return this.store.statementsMatching(x, null, null, null).length !== 0 || // Don't pin fresh bnodes
359
371
  this.store.statementsMatching(null, x).length !== 0 ||
360
372
  this.store.statementsMatching(null, null, x).length !== 0
@@ -363,7 +375,7 @@ flagAuthorizationMetadata () {
363
375
  /**
364
376
  * @private
365
377
  */
366
- bnodeContext (bnodes, doc) {
378
+ bnodeContext(bnodes, doc) {
367
379
  var context = []
368
380
  if (bnodes.length) {
369
381
  this.cacheIfps()
@@ -380,7 +392,7 @@ flagAuthorizationMetadata () {
380
392
  * Returns the best context for a single statement
381
393
  * @private
382
394
  */
383
- statementContext (st: Quad) {
395
+ statementContext(st: Quad) {
384
396
  var bnodes = this.statementBnodes(st)
385
397
  return this.bnodeContext(bnodes, st.graph)
386
398
  }
@@ -388,7 +400,7 @@ flagAuthorizationMetadata () {
388
400
  /**
389
401
  * @private
390
402
  */
391
- contextWhere (context) {
403
+ contextWhere(context) {
392
404
  var updater = this
393
405
  return (!context || context.length === 0)
394
406
  ? ''
@@ -401,7 +413,7 @@ flagAuthorizationMetadata () {
401
413
  /**
402
414
  * @private
403
415
  */
404
- fire (
416
+ fire(
405
417
  uri: string,
406
418
  query: string,
407
419
  callbackFunction: CallBackFunction,
@@ -415,7 +427,7 @@ flagAuthorizationMetadata () {
415
427
  // console.log('UpdateManager: sending update to <' + uri + '>')
416
428
 
417
429
  options.noMeta = true;
418
- options.contentType = 'application/sparql-update';
430
+ options.contentType = options.contentType || 'application/sparql-update';
419
431
  options.body = query;
420
432
 
421
433
  return this.store.fetcher.webOperation('PATCH', uri, options)
@@ -438,7 +450,7 @@ flagAuthorizationMetadata () {
438
450
  })
439
451
  }
440
452
 
441
- // ARE THESE THEE FUNCTIONS USED? DEPROCATE?
453
+ // ARE THESE THREE FUNCTIONS USED? DEPRECATE?
442
454
 
443
455
  /** return a statemnet updating function
444
456
  *
@@ -446,7 +458,7 @@ flagAuthorizationMetadata () {
446
458
  * It returns an object which includes
447
459
  * function which can be used to change the object of the statement.
448
460
  */
449
- update_statement (statement: Quad) {
461
+ update_statement(statement: Quad) {
450
462
  if (statement && !statement.graph) {
451
463
  return
452
464
  }
@@ -474,7 +486,7 @@ flagAuthorizationMetadata () {
474
486
  }
475
487
  }
476
488
 
477
- insert_statement (st: Quad, callbackFunction: CallBackFunction): void {
489
+ insert_statement(st: Quad, callbackFunction: CallBackFunction): void {
478
490
  var st0 = st instanceof Array ? st[0] : st
479
491
  var query = this.contextWhere(this.statementContext(st0))
480
492
 
@@ -492,7 +504,7 @@ flagAuthorizationMetadata () {
492
504
  this.fire(st0.graph.value, query, callbackFunction)
493
505
  }
494
506
 
495
- delete_statement (st: Quad | Quad[], callbackFunction: CallBackFunction): void {
507
+ delete_statement(st: Quad | Quad[], callbackFunction: CallBackFunction): void {
496
508
  var st0 = st instanceof Array ? st[0] : st
497
509
  var query = this.contextWhere(this.statementContext(st0))
498
510
 
@@ -510,7 +522,7 @@ flagAuthorizationMetadata () {
510
522
  this.fire(st0.graph.value, query, callbackFunction)
511
523
  }
512
524
 
513
- /// //////////////////////
525
+ /// //////////////////////
514
526
 
515
527
  /**
516
528
  * Requests a now or future action to refresh changes coming downstream
@@ -521,7 +533,7 @@ flagAuthorizationMetadata () {
521
533
  * @param doc
522
534
  * @param action
523
535
  */
524
- requestDownstreamAction (doc: NamedNode, action): void {
536
+ requestDownstreamAction(doc: NamedNode, action): void {
525
537
  var control = this.patchControlFor(doc)
526
538
  if (!control.pendingUpstream) {
527
539
  action(doc)
@@ -540,18 +552,18 @@ flagAuthorizationMetadata () {
540
552
  * We want to start counting websocket notifications
541
553
  * to distinguish the ones from others from our own.
542
554
  */
543
- clearUpstreamCount (doc: NamedNode): void {
555
+ clearUpstreamCount(doc: NamedNode): void {
544
556
  var control = this.patchControlFor(doc)
545
557
  control.upstreamCount = 0
546
558
  }
547
559
 
548
- getUpdatesVia (doc: NamedNode): string | null {
560
+ getUpdatesVia(doc: NamedNode): string | null {
549
561
  var linkHeaders = this.store.fetcher.getHeader(doc, 'updates-via')
550
562
  if (!linkHeaders || !linkHeaders.length) return null
551
563
  return linkHeaders[0].trim()
552
564
  }
553
565
 
554
- addDownstreamChangeListener (doc: NamedNode, listener): void {
566
+ addDownstreamChangeListener(doc: NamedNode, listener): void {
555
567
  var control = this.patchControlFor(doc)
556
568
  if (!control.downstreamChangeListeners) { control.downstreamChangeListeners = [] }
557
569
  control.downstreamChangeListeners.push(listener)
@@ -560,7 +572,7 @@ flagAuthorizationMetadata () {
560
572
  })
561
573
  }
562
574
 
563
- reloadAndSync (doc: NamedNode): void {
575
+ reloadAndSync(doc: NamedNode): void {
564
576
  var control = this.patchControlFor(doc)
565
577
  var updater = this
566
578
 
@@ -582,7 +594,7 @@ flagAuthorizationMetadata () {
582
594
  }
583
595
  }
584
596
  control.reloading = false
585
- if (control.outOfDate){
597
+ if (control.outOfDate) {
586
598
  // console.log(' Extra reload because of extra update.')
587
599
  control.outOfDate = false
588
600
  tryReload()
@@ -622,7 +634,7 @@ flagAuthorizationMetadata () {
622
634
  *
623
635
  * @returns {boolean}
624
636
  */
625
- setRefreshHandler (doc: NamedNode, handler): boolean {
637
+ setRefreshHandler(doc: NamedNode, handler): boolean {
626
638
  let wssURI = this.getUpdatesVia(doc) // relative
627
639
  // var kb = this.store
628
640
  var theHandler = handler
@@ -663,7 +675,7 @@ flagAuthorizationMetadata () {
663
675
  var control = self.patchControlFor(doc)
664
676
  control.upstreamCount = 0
665
677
 
666
- socket.onerror = function onerror (err: Error) {
678
+ socket.onerror = function onerror(err: Error) {
667
679
  // console.log('Error on Websocket:', err)
668
680
  }
669
681
 
@@ -728,8 +740,8 @@ flagAuthorizationMetadata () {
728
740
  const thisUpdater = this
729
741
  const uniqueDocs: Array<NamedNode> = []
730
742
  docs.forEach(doc => {
731
- if (!uniqueDocs.find(uniqueDoc => uniqueDoc.equals(doc))) uniqueDocs.push(doc as NamedNode)
732
- })
743
+ if (!uniqueDocs.find(uniqueDoc => uniqueDoc.equals(doc))) uniqueDocs.push(doc as NamedNode)
744
+ })
733
745
  const updates = uniqueDocs.map(doc =>
734
746
  thisUpdater.update(deletions.filter(st => st.why.equals(doc)),
735
747
  insertions.filter(st => st.why.equals(doc))))
@@ -740,7 +752,103 @@ flagAuthorizationMetadata () {
740
752
  }
741
753
 
742
754
  /**
743
- * This high-level function updates the local store iff the web is changed successfully.
755
+ * @private
756
+ *
757
+ * This helper function constructs SPARQL Update query from resolved arguments.
758
+ *
759
+ * @param ds: deletions array.
760
+ * @param is: insertions array.
761
+ * @param bnodes_context: Additional context to uniquely identify any blank nodes.
762
+ */
763
+ constructSparqlUpdateQuery(
764
+ ds: ReadonlyArray<Statement>,
765
+ is: ReadonlyArray<Statement>,
766
+ bnodes_context,
767
+ ): string {
768
+ var whereClause = this.contextWhere(bnodes_context)
769
+ var query = ''
770
+ if (whereClause.length) { // Is there a WHERE clause?
771
+ if (ds.length) {
772
+ query += 'DELETE { '
773
+ for (let i = 0; i < ds.length; i++) {
774
+ query += this.anonymizeNT(ds[i]) + '\n'
775
+ }
776
+ query += ' }\n'
777
+ }
778
+ if (is.length) {
779
+ query += 'INSERT { '
780
+ for (let i = 0; i < is.length; i++) {
781
+ query += this.anonymizeNT(is[i]) + '\n'
782
+ }
783
+ query += ' }\n'
784
+ }
785
+ query += whereClause
786
+ } else { // no where clause
787
+ if (ds.length) {
788
+ query += 'DELETE DATA { '
789
+ for (let i = 0; i < ds.length; i++) {
790
+ query += this.anonymizeNT(ds[i]) + '\n'
791
+ }
792
+ query += ' } \n'
793
+ }
794
+ if (is.length) {
795
+ if (ds.length) query += ' ; '
796
+ query += 'INSERT DATA { '
797
+ for (let i = 0; i < is.length; i++) {
798
+ query += this.nTriples(is[i]) + '\n'
799
+ }
800
+ query += ' }\n'
801
+ }
802
+ }
803
+ return query;
804
+ }
805
+
806
+ /**
807
+ * @private
808
+ *
809
+ * This helper function constructs n3-patch query from resolved arguments.
810
+ *
811
+ * @param ds: deletions array.
812
+ * @param is: insertions array.
813
+ * @param bnodes_context: Additional context to uniquely identify any blanknodes.
814
+ */
815
+ constructN3PatchQuery(
816
+ ds: ReadonlyArray<Statement>,
817
+ is: ReadonlyArray<Statement>,
818
+ bnodes_context,
819
+ ): string {
820
+ var query = `
821
+ @prefix solid: <http://www.w3.org/ns/solid/terms#>.
822
+ @prefix ex: <http://www.example.org/terms#>.
823
+
824
+ _:patch
825
+ `;
826
+ // If bnode context is non trivial, express it as ?conditions formula.
827
+ if (bnodes_context && bnodes_context.length > 0) {
828
+ query += `
829
+ solid:where {
830
+ ${bnodes_context.map((x) => this.anonymizeNT(x)).join('\n ')}
831
+ };`
832
+ }
833
+ if (ds.length > 0) {
834
+ query += `
835
+ solid:deletes {
836
+ ${ds.map((x) => this.anonymizeNT(x)).join('\n ')}
837
+ };`
838
+ }
839
+ if (is.length > 0) {
840
+ query += `
841
+ solid:inserts {
842
+ ${is.map((x) => this.anonymizeNT(x)).join('\n ')}
843
+ };`
844
+ }
845
+ query += " a solid:InsertDeletePatch .\n"
846
+
847
+ return query;
848
+ }
849
+
850
+ /**
851
+ * This high-level function updates the local store if the web is changed successfully.
744
852
  * Deletions, insertions may be undefined or single statements or lists or formulae (may contain bnodes which can be indirectly identified by a where clause).
745
853
  * The `why` property of each statement must be the same and give the web document to be updated.
746
854
  * @param deletions - Statement or statements to be deleted.
@@ -750,16 +858,16 @@ flagAuthorizationMetadata () {
750
858
  * @param options - Options for the fetch call
751
859
  */
752
860
  update(
753
- deletions: ReadonlyArray<Statement>,
754
- insertions: ReadonlyArray<Statement>,
755
- callback?: (
756
- uri: string | undefined | null,
757
- success: boolean,
758
- errorBody?: string,
759
- response?: Response | Error
760
- ) => void,
761
- secondTry?: boolean,
762
- options: Options = {}
861
+ deletions: ReadonlyArray<Statement>,
862
+ insertions: ReadonlyArray<Statement>,
863
+ callback?: (
864
+ uri: string | undefined | null,
865
+ success: boolean,
866
+ errorBody?: string,
867
+ response?: Response | Error
868
+ ) => void,
869
+ secondTry?: boolean,
870
+ options: Options = {}
763
871
  ): void | Promise<void> {
764
872
  if (!callback) {
765
873
  var thisUpdater = this
@@ -778,10 +886,10 @@ flagAuthorizationMetadata () {
778
886
  var kb = this.store
779
887
  var ds = !deletions ? []
780
888
  : isStore(deletions) ? deletions.statements
781
- : deletions instanceof Array ? deletions : [ deletions ]
889
+ : deletions instanceof Array ? deletions : [deletions]
782
890
  var is = !insertions ? []
783
891
  : isStore(insertions) ? insertions.statements
784
- : insertions instanceof Array ? insertions : [ insertions ]
892
+ : insertions instanceof Array ? insertions : [insertions]
785
893
  if (!(ds instanceof Array)) {
786
894
  throw new Error('Type Error ' + (typeof ds) + ': ' + ds)
787
895
  }
@@ -823,70 +931,42 @@ flagAuthorizationMetadata () {
823
931
  })
824
932
  })
825
933
 
826
- var protocol = this.editable(doc.value, kb)
934
+ var protocol = this.editable(doc.value, kb);
935
+
827
936
  if (protocol === false) {
828
937
  throw new Error('Update: Can\'t make changes in uneditable ' + doc)
829
938
  }
830
939
  if (protocol === undefined) { // Not enough metadata
831
940
  if (secondTry) {
832
- throw new Error('Update: Loaded ' + doc + "but stil can't figure out what editing protcol it supports.")
941
+ throw new Error('Update: Loaded ' + doc + "but still can't figure out what editing protocol it supports.")
833
942
  }
834
943
  // console.log(`Update: have not loaded ${doc} before: loading now...`);
835
944
  (this.store.fetcher.load(doc as NamedNode) as Promise<Response>).then(response => {
836
945
  this.update(deletions, insertions, callback, true, options)
837
946
  }, err => {
838
- if (err.response.status === 404) { // nonexistent files are fine
839
- this.update(deletions, insertions, callback, true, options)
840
- } else {
841
- throw new Error(`Update: Can't get updatability status ${doc} before patching: ${err}`)
842
- }
947
+ if (err.response.status === 404) { // nonexistent files are fine
948
+ this.update(deletions, insertions, callback, true, options)
949
+ } else {
950
+ throw new Error(`Update: Can't get updatability status ${doc} before patching: ${err}`)
951
+ }
843
952
  })
844
953
  return
845
- } else if ((protocol as string).indexOf('SPARQL') >= 0) {
954
+ } else if ((protocol as string).indexOf('SPARQL') >= 0 || (protocol as string).indexOf('N3PATCH') >= 0) {
955
+ var isSparql = (protocol as string).indexOf('SPARQL') >= 0
956
+
846
957
  var bnodes: BlankNode[] = []
847
958
  // change ReadOnly type to Mutable type
848
959
  type Mutable<Type> = {
849
960
  -readonly [Key in keyof Type]: Type[Key];
850
961
  }
851
-
962
+
852
963
  if (ds.length) bnodes = this.statementArrayBnodes(ds as Mutable<typeof ds>)
853
964
  if (is.length) bnodes = bnodes.concat(this.statementArrayBnodes(is as Mutable<typeof is>))
854
965
  var context = this.bnodeContext(bnodes, doc)
855
- var whereClause = this.contextWhere(context)
856
- var query = ''
857
- if (whereClause.length) { // Is there a WHERE clause?
858
- if (ds.length) {
859
- query += 'DELETE { '
860
- for (let i = 0; i < ds.length; i++) {
861
- query += this.anonymizeNT(ds[i]) + '\n'
862
- }
863
- query += ' }\n'
864
- }
865
- if (is.length) {
866
- query += 'INSERT { '
867
- for (let i = 0; i < is.length; i++) {
868
- query += this.anonymizeNT(is[i]) + '\n'
869
- }
870
- query += ' }\n'
871
- }
872
- query += whereClause
873
- } else { // no where clause
874
- if (ds.length) {
875
- query += 'DELETE DATA { '
876
- for (let i = 0; i < ds.length; i++) {
877
- query += this.anonymizeNT(ds[i]) + '\n'
878
- }
879
- query += ' } \n'
880
- }
881
- if (is.length) {
882
- if (ds.length) query += ' ; '
883
- query += 'INSERT DATA { '
884
- for (let i = 0; i < is.length; i++) {
885
- query += this.nTriples(is[i]) + '\n'
886
- }
887
- query += ' }\n'
888
- }
889
- }
966
+
967
+ var query = isSparql ? this.constructSparqlUpdateQuery(ds, is, context) : this.constructN3PatchQuery(ds, is, context);
968
+ options.contentType = isSparql ? 'application/sparql-update' : 'text/n3'
969
+
890
970
  // Track pending upstream patches until they have finished their callbackFunction
891
971
  control.pendingUpstream = control.pendingUpstream ? control.pendingUpstream + 1 : 1
892
972
  if ('upstreamCount' in control) {
@@ -944,7 +1024,7 @@ flagAuthorizationMetadata () {
944
1024
  }
945
1025
  }
946
1026
 
947
- updateDav (
1027
+ updateDav(
948
1028
  doc: Quad_Subject,
949
1029
  ds,
950
1030
  is,
@@ -962,7 +1042,7 @@ flagAuthorizationMetadata () {
962
1042
  if (!response) {
963
1043
  return null // throw "No record HTTP GET response for document: "+doc
964
1044
  }
965
- var contentType = (kb.the(response, this.ns.httph('content-type'))as Term).value
1045
+ var contentType = (kb.the(response, this.ns.httph('content-type')) as Term).value
966
1046
 
967
1047
  // prepare contents of revised document
968
1048
  let newSts = kb.statementsMatching(undefined, undefined, undefined, doc).slice() // copy!
@@ -1015,7 +1095,7 @@ flagAuthorizationMetadata () {
1015
1095
  * @param callbackFunction
1016
1096
  * @param options
1017
1097
  */
1018
- updateLocalFile (doc: NamedNode, ds, is, callbackFunction, options: Options = {}): void {
1098
+ updateLocalFile(doc: NamedNode, ds, is, callbackFunction, options: Options = {}): void {
1019
1099
  const kb = this.store
1020
1100
  // console.log('Writing back to local file\n')
1021
1101
 
@@ -1023,10 +1103,10 @@ flagAuthorizationMetadata () {
1023
1103
  let newSts = kb.statementsMatching(undefined, undefined, undefined, doc).slice() // copy!
1024
1104
 
1025
1105
  for (let i = 0; i < ds.length; i++) {
1026
- Util.RDFArrayRemove(newSts, ds[ i ])
1106
+ Util.RDFArrayRemove(newSts, ds[i])
1027
1107
  }
1028
1108
  for (let i = 0; i < is.length; i++) {
1029
- newSts.push(is[ i ])
1109
+ newSts.push(is[i])
1030
1110
  }
1031
1111
  // serialize to the appropriate format
1032
1112
  var dot = doc.value.lastIndexOf('.')
@@ -1035,7 +1115,7 @@ flagAuthorizationMetadata () {
1035
1115
  }
1036
1116
  var ext = doc.value.slice(dot + 1)
1037
1117
 
1038
- let contentType = Fetcher.CONTENT_TYPE_BY_EXT[ ext ]
1118
+ let contentType = Fetcher.CONTENT_TYPE_BY_EXT[ext]
1039
1119
  if (!contentType) {
1040
1120
  throw new Error('File extension .' + ext + ' not supported for data write')
1041
1121
  }
@@ -1043,8 +1123,8 @@ flagAuthorizationMetadata () {
1043
1123
  options.body = this.serialize(doc.value, newSts, contentType);
1044
1124
  options.contentType = contentType;
1045
1125
 
1046
- kb.fetcher.webOperation('PUT', doc.value, options).then( (response)=>{
1047
- if(!response.ok) return callbackFunction(doc.value,false,response.error)
1126
+ kb.fetcher.webOperation('PUT', doc.value, options).then((response) => {
1127
+ if (!response.ok) return callbackFunction(doc.value, false, response.error)
1048
1128
  for (let i = 0; i < ds.length; i++) {
1049
1129
  kb.remove(ds[i]);
1050
1130
  }
@@ -1060,7 +1140,7 @@ flagAuthorizationMetadata () {
1060
1140
  *
1061
1141
  * @returns {string}
1062
1142
  */
1063
- serialize (uri: string, data: string | Quad[], contentType: string): string {
1143
+ serialize(uri: string, data: string | Quad[], contentType: string): string {
1064
1144
  const kb = this.store
1065
1145
  let documentString
1066
1146
 
@@ -1143,7 +1223,7 @@ flagAuthorizationMetadata () {
1143
1223
  * @param doc {RDFlibNamedNode}
1144
1224
  * @param callbackFunction
1145
1225
  */
1146
- reload (
1226
+ reload(
1147
1227
  kb: IndexedFormula,
1148
1228
  doc: docReloadType,
1149
1229
  callbackFunction: (ok: boolean, message?: string, response?: Error | Response) => {} | void
@@ -1163,8 +1243,8 @@ flagAuthorizationMetadata () {
1163
1243
  //@ts-ignore Where does onErrorWasCalled come from?
1164
1244
  } else if (response.onErrorWasCalled || response.status !== 200) {
1165
1245
  // console.log(' Non-HTTP error reloading data! onErrorWasCalled=' +
1166
- //@ts-ignore Where does onErrorWasCalled come from?
1167
- // response.onErrorWasCalled + ' status: ' + response.status)
1246
+ //@ts-ignore Where does onErrorWasCalled come from?
1247
+ // response.onErrorWasCalled + ' status: ' + response.status)
1168
1248
  callbackFunction(false, 'Non-HTTP error reloading data: ' + body, response)
1169
1249
  } else {
1170
1250
  var elapsedTimeMs = Date.now() - startTime
@@ -1176,8 +1256,8 @@ flagAuthorizationMetadata () {
1176
1256
  doc.reloadTimeCount += 1
1177
1257
 
1178
1258
  // console.log(' Fetch took ' + elapsedTimeMs + 'ms, av. of ' +
1179
- // doc.reloadTimeCount + ' = ' +
1180
- // (doc.reloadTimeTotal / doc.reloadTimeCount) + 'ms.')
1259
+ // doc.reloadTimeCount + ' = ' +
1260
+ // (doc.reloadTimeTotal / doc.reloadTimeCount) + 'ms.')
1181
1261
 
1182
1262
  callbackFunction(true)
1183
1263
  }