n3 3.0.0-alpha.5 → 3.0.0-alpha.7

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/src/N3Store.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // **N3Store** objects store N3 quads by graph in memory.
2
- import { Readable } from 'readable-stream';
2
+ import { Readable, finished } from 'readable-stream';
3
3
  import { default as N3DataFactory, termToId, termFromId } from './N3DataFactory';
4
4
  import namespaces from './IRIs';
5
5
  import { isDefaultGraph } from './N3Util';
@@ -603,10 +603,55 @@ export default class N3Store {
603
603
  return !this.readQuads(subjectOrQuad, predicate, object, graph).next().done;
604
604
  }
605
605
 
606
- // ### `import` adds a stream of quads to the store
606
+ // ### `import` adds a stream of quads to the store.
607
+ // It returns the stream, wrapped such that it can also be awaited
608
+ // as a promise of the store (per the RDF/JS `Dataset.import` signature).
607
609
  import(stream) {
608
610
  stream.on('data', quad => { this.addQuad(quad); });
609
- return stream;
611
+
612
+ // Only track completion once awaited, so unawaited imports keep the stream's behavior
613
+ const store = this;
614
+ let promise = null;
615
+ function completion() {
616
+ return promise || (promise = new Promise((resolve, reject) => {
617
+ let stopListening = null;
618
+ function settle(error) {
619
+ stopListening();
620
+ error ? reject(error) : resolve(store);
621
+ }
622
+ try {
623
+ stopListening = finished(stream, { readable: true, writable: false }, settle);
624
+ }
625
+ // RDF/JS streams that are not Node.js streams only signal their end and errors
626
+ // and must be awaited before they finish
627
+ catch {
628
+ stopListening = () => {
629
+ if (stream.removeListener) {
630
+ stream.removeListener('end', onFinish);
631
+ stream.removeListener('error', settle);
632
+ }
633
+ };
634
+ function onFinish() { settle(); }
635
+ stream.on('end', onFinish);
636
+ stream.on('error', settle);
637
+ }
638
+ }));
639
+ }
640
+ const thenable = {
641
+ then(onFulfilled, onRejected) { return completion().then(onFulfilled, onRejected); },
642
+ catch(onRejected) { return completion().catch(onRejected); },
643
+ finally(onFinally) { return completion().finally(onFinally); },
644
+ };
645
+ // Return a wrapper that behaves as the stream, but is also awaitable
646
+ return new Proxy(stream, {
647
+ get(target, property) {
648
+ if (property === 'then' || property === 'catch' || property === 'finally')
649
+ return thenable[property];
650
+ const value = target[property];
651
+ // Methods run on the stream itself, which may rely on private fields
652
+ return typeof value === 'function' ? value.bind(target) : value;
653
+ },
654
+ });
610
655
  }
611
656
 
612
657
  // ### `removeQuad` removes a quad from the store if it exists
@@ -649,9 +694,12 @@ export default class N3Store {
649
694
  }
650
695
 
651
696
  // ### `removeQuads` removes multiple quads from the store
697
+ // returns `true` if all quads were removed, `false` if some were not found
652
698
  removeQuads(quads) {
699
+ let removed = true;
653
700
  for (let i = 0; i < quads.length; i++)
654
- this.removeQuad(quads[i]);
701
+ removed = this.removeQuad(quads[i]) && removed;
702
+ return removed;
655
703
  }
656
704
 
657
705
  // ### `remove` removes a stream of quads from the store
package/src/N3Writer.js CHANGED
@@ -24,6 +24,9 @@ const escape = /["\\\t\n\r\b\f\u0000-\u0019\ud800-\udbff]/,
24
24
  '\n': '\\n', '\r': '\\r', '\b': '\\b', '\f': '\\f',
25
25
  };
26
26
 
27
+ // Characters that a version label cannot contain
28
+ const invalidVersionLabel = /["\\\u0000-\u001f\u007f]|[\ud800-\udbff](?![\udc00-\udfff])|(?:^|[^\ud800-\udbff])[\udc00-\udfff]/;
29
+
27
30
  // ## Placeholder class to represent already pretty-printed terms
28
31
  class SerializedTerm extends Term {
29
32
  // Pretty-printed nodes are not equal to any other node
@@ -60,12 +63,19 @@ export default class N3Writer {
60
63
  this._endStream = options.end === undefined ? true : !!options.end;
61
64
  }
62
65
 
66
+ // A version label is written as-is, so it cannot contain characters that need escaping,
67
+ // nor unpaired surrogates (which cannot be encoded)
68
+ if (options.version && invalidVersionLabel.test(options.version))
69
+ throw new Error(`Invalid version label: ${JSON.stringify(options.version)}`);
70
+
63
71
  // Initialize writer, depending on the format
64
72
  this._subject = null;
65
73
  if (!(/triple|quad/i).test(options.format)) {
66
74
  this._lineMode = false;
67
75
  this._escape = escape, this._escapeAll = escapeAll, this._characterReplacer = characterReplacer;
68
76
  this._graph = DEFAULTGRAPH;
77
+ if (options.version)
78
+ this._write(`@version "${options.version}".\n`);
69
79
  this._prefixIRIs = Object.create(null);
70
80
  // Escaped prefix IRIs and names for the prefix matcher, computed once per prefix
71
81
  this._prefixPatterns = Object.create(null);
@@ -82,6 +92,8 @@ export default class N3Writer {
82
92
  // N-Triples and N-Quads are written in their canonical form
83
93
  this._escape = canonicalEscape, this._escapeAll = canonicalEscapeAll;
84
94
  this._characterReplacer = canonicalCharacterReplacer;
95
+ if (options.version)
96
+ this._write(`VERSION "${options.version}"\n`);
85
97
  }
86
98
  }
87
99
 
@@ -97,6 +109,15 @@ export default class N3Writer {
97
109
  this._outputStream.write(string, 'utf8', callback);
98
110
  }
99
111
 
112
+ // ### `_endStatement` finishes a pending statement and closes an open graph block
113
+ _endStatement() {
114
+ if (this._subject !== null) {
115
+ this._write(this._inDefaultGraph ? '.\n' : '\n}\n');
116
+ this._subject = null;
117
+ this._graph = DEFAULTGRAPH;
118
+ }
119
+ }
120
+
100
121
  // ### `_writeQuad` writes the quad to the output stream
101
122
  _writeQuad(subject, predicate, object, graph, done) {
102
123
  try {
@@ -324,10 +345,7 @@ export default class N3Writer {
324
345
  iri = iri.value;
325
346
  hasPrefixes = true;
326
347
  // Finish a possible pending quad
327
- if (this._subject !== null) {
328
- this._write(this._inDefaultGraph ? '.\n' : '\n}\n');
329
- this._subject = null, this._graph = '';
330
- }
348
+ this._endStatement();
331
349
  // Store and write the prefix
332
350
  this._prefixIRIs[iri] = (prefix += ':');
333
351
  this._prefixPatterns[iri] = escapeRegex(iri);
@@ -406,10 +424,7 @@ export default class N3Writer {
406
424
  // ### `end` signals the end of the output stream
407
425
  end(done) {
408
426
  // Finish a possible pending quad
409
- if (this._subject !== null) {
410
- this._write(this._inDefaultGraph ? '.\n' : '\n}\n');
411
- this._subject = null;
412
- }
427
+ this._endStatement();
413
428
  // Disallow further writing
414
429
  this._write = this._blockedWrite;
415
430