n3 2.2.9 → 2.2.11

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/lib/N3Store.js CHANGED
@@ -14,14 +14,22 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
14
14
  // **N3Store** objects store N3 quads by graph in memory.
15
15
 
16
16
  const ITERATOR = Symbol('iter');
17
+ const SIZE = Symbol('size');
17
18
  function hasInIndex(index0, key0, key1, key2) {
18
19
  const index1 = index0 && index0[key0];
19
20
  const index2 = index1 && index1[key1];
20
21
  return !!index2 && key2 in index2;
21
22
  }
22
23
  function merge(target, source, depth = 4) {
23
- if (depth === 0) return Object.assign(target, source);
24
- for (const key in source) target[key] = merge(target[key] || Object.create(null), source[key], depth - 1);
24
+ let size = target[SIZE] || 0;
25
+ for (const key in source) {
26
+ if (!(key in target)) {
27
+ size++;
28
+ target[key] = depth === 0 ? null : merge(Object.create(null), source[key], depth - 1);
29
+ } else if (depth !== 0) target[key] = merge(target[key], source[key], depth - 1);
30
+ }
31
+ // Depth 2 is the level of the `subjects`, `predicates`, and `objects` indexes.
32
+ if (depth <= 2) target[SIZE] = size;
25
33
  return target;
26
34
  }
27
35
 
@@ -33,13 +41,16 @@ function merge(target, source, depth = 4) {
33
41
  * *not* be set as the value for an index.
34
42
  */
35
43
  function intersect(s1, s2, depth = 4) {
36
- let target = false;
44
+ let target = false,
45
+ size = 0;
46
+ if (depth <= 2 && s2[SIZE] < s1[SIZE]) [s1, s2] = [s2, s1];
37
47
  for (const key in s1) {
38
48
  if (key in s2) {
39
49
  const intersection = depth === 0 ? null : intersect(s1[key], s2[key], depth - 1);
40
50
  if (intersection !== false) {
41
51
  target = target || Object.create(null);
42
52
  target[key] = intersection;
53
+ size++;
43
54
  }
44
55
  // Depth 3 is the 'subjects', 'predicates' and 'objects' keys.
45
56
  // If the 'subjects' index is empty, so will the 'predicates' and 'objects' index.
@@ -48,6 +59,9 @@ function intersect(s1, s2, depth = 4) {
48
59
  }
49
60
  }
50
61
  }
62
+
63
+ // Depth 2 is the level of the `subjects`, `predicates`, and `objects` indexes.
64
+ if (depth <= 2 && target) target[SIZE] = size;
51
65
  return target;
52
66
  }
53
67
 
@@ -59,18 +73,21 @@ function intersect(s1, s2, depth = 4) {
59
73
  * *not* be set as the value for an index.
60
74
  */
61
75
  function difference(s1, s2, depth = 4) {
62
- let target = false;
76
+ let target = false,
77
+ size = 0;
63
78
  for (const key in s1) {
64
79
  // When the key is not in the index, then none of the triples defined by s1[key] are
65
80
  // in s2 and so we want to copy them over to the resultant store.
66
81
  if (!(key in s2)) {
67
82
  target = target || Object.create(null);
68
83
  target[key] = depth === 0 ? null : merge({}, s1[key], depth - 1);
84
+ size++;
69
85
  } else if (depth !== 0) {
70
86
  const diff = difference(s1[key], s2[key], depth - 1);
71
87
  if (diff !== false) {
72
88
  target = target || Object.create(null);
73
89
  target[key] = diff;
90
+ size++;
74
91
  }
75
92
  // Depth 3 is the 'subjects', 'predicates' and 'objects' keys.
76
93
  // If the 'subjects' index is empty, so will the 'predicates' and 'objects' index.
@@ -79,6 +96,9 @@ function difference(s1, s2, depth = 4) {
79
96
  }
80
97
  }
81
98
  }
99
+
100
+ // Depth 2 is the level of the `subjects`, `predicates`, and `objects` indexes.
101
+ if (depth <= 2 && target) target[SIZE] = size;
82
102
  return target;
83
103
  }
84
104
 
@@ -178,7 +198,7 @@ class N3Store {
178
198
  size = 0;
179
199
  const graphs = this._graphs;
180
200
  let subjects, subject;
181
- for (const graphKey in graphs) for (const subjectKey in subjects = graphs[graphKey].subjects) for (const predicateKey in subject = subjects[subjectKey]) size += Object.keys(subject[predicateKey]).length;
201
+ for (const graphKey in graphs) for (const subjectKey in subjects = graphs[graphKey].subjects) for (const predicateKey in subject = subjects[subjectKey]) size += subject[predicateKey][SIZE];
182
202
  return this._size = size;
183
203
  }
184
204
 
@@ -187,12 +207,27 @@ class N3Store {
187
207
  // ### `_addToIndex` adds a quad to a three-layered index.
188
208
  // Returns if the index has changed, if the entry did not already exist.
189
209
  _addToIndex(index0, key0, key1, key2) {
190
- // Create layers as necessary
191
- const index1 = index0[key0] || (index0[key0] = {});
192
- const index2 = index1[key1] || (index1[key1] = {});
210
+ // Create layers as necessary, maintaining their entry counters
211
+ let index1 = index0[key0];
212
+ if (!index1) {
213
+ index0[key0] = index1 = {
214
+ [SIZE]: 0
215
+ };
216
+ index0[SIZE]++;
217
+ }
218
+ let index2 = index1[key1];
219
+ if (!index2) {
220
+ index1[key1] = index2 = {
221
+ [SIZE]: 0
222
+ };
223
+ index1[SIZE]++;
224
+ }
193
225
  // Setting the key to _any_ value signals the presence of the quad
194
226
  const existed = key2 in index2;
195
- if (!existed) index2[key2] = null;
227
+ if (!existed) {
228
+ index2[key2] = null;
229
+ index2[SIZE]++;
230
+ }
196
231
  return !existed;
197
232
  }
198
233
 
@@ -203,11 +238,13 @@ class N3Store {
203
238
  index2 = index1[key1];
204
239
  delete index2[key2];
205
240
 
206
- // Remove intermediary index layers if they are empty
207
- for (const key in index2) return;
241
+ // Remove intermediary index layers if they are empty,
242
+ // which the entry counters detect in constant time
243
+ if (--index2[SIZE] !== 0) return;
208
244
  delete index1[key1];
209
- for (const key in index1) return;
245
+ if (--index1[SIZE] !== 0) return;
210
246
  delete index0[key0];
247
+ index0[SIZE]--;
211
248
  }
212
249
 
213
250
  // ### `_findInIndex` finds a set of quads in a three-layered index.
@@ -312,7 +349,7 @@ class N3Store {
312
349
  // If a key is specified, count the quad if it exists
313
350
  if (key2) key2 in index2 && count++;
314
351
  // Otherwise, count all quads
315
- else count += Object.keys(index2).length;
352
+ else count += index2[SIZE];
316
353
  }
317
354
  }
318
355
  }
@@ -365,9 +402,15 @@ class N3Store {
365
402
  // Create the graph if it doesn't exist yet
366
403
  if (!graphItem) {
367
404
  graphItem = this._graphs[graph] = {
368
- subjects: {},
369
- predicates: {},
370
- objects: {}
405
+ subjects: {
406
+ [SIZE]: 0
407
+ },
408
+ predicates: {
409
+ [SIZE]: 0
410
+ },
411
+ objects: {
412
+ [SIZE]: 0
413
+ }
371
414
  };
372
415
  // Freezing a graph helps subsequent `add` performance,
373
416
  // and properties will never be modified anyway
@@ -454,8 +497,7 @@ class N3Store {
454
497
  if (this._size !== null) this._size--;
455
498
 
456
499
  // Remove the graph if it is empty
457
- for (subject in graphItem.subjects) return true;
458
- delete graphs[graph];
500
+ if (graphItem.subjects[SIZE] === 0) delete graphs[graph];
459
501
  return true;
460
502
  }
461
503
 
@@ -1062,7 +1104,8 @@ exports.default = N3Store;
1062
1104
  function indexMatch(index, ids, depth = 0) {
1063
1105
  const ind = ids[depth];
1064
1106
  if (ind && !(ind in index)) return false;
1065
- let target = false;
1107
+ let target = false,
1108
+ size = 0;
1066
1109
  for (const key in ind ? {
1067
1110
  [ind]: index[ind]
1068
1111
  } : index) {
@@ -1070,8 +1113,10 @@ function indexMatch(index, ids, depth = 0) {
1070
1113
  if (result !== false) {
1071
1114
  target = target || Object.create(null);
1072
1115
  target[key] = result;
1116
+ size++;
1073
1117
  }
1074
1118
  }
1119
+ if (target) target[SIZE] = size;
1075
1120
  return target;
1076
1121
  }
1077
1122
 
@@ -9,6 +9,9 @@ var _N3Writer = _interopRequireDefault(require("./N3Writer"));
9
9
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
10
  // **N3StreamWriter** serializes a quad stream into a text stream.
11
11
 
12
+ const MIN_CHUNK_SIZE = 16 * 1024;
13
+ const DEFAULT_FLUSH_DELAY_MS = 20;
14
+
12
15
  // ## Constructor
13
16
  class N3StreamWriter extends _readableStream.Transform {
14
17
  constructor(options) {
@@ -17,27 +20,77 @@ class N3StreamWriter extends _readableStream.Transform {
17
20
  writableObjectMode: true
18
21
  });
19
22
 
23
+ // Coalesce serialized fragments into larger stream chunks
24
+ this._buffer = '';
25
+
26
+ // Flush partial chunks after a bounded delay
27
+ this._flushTimer = null;
28
+ this._flushDelay = options && options.flushDelay !== undefined ? options.flushDelay : DEFAULT_FLUSH_DELAY_MS;
29
+
20
30
  // Set up writer with a dummy stream object
21
31
  const writer = this._writer = new _N3Writer.default({
22
- write: (quad, encoding, callback) => {
23
- this.push(quad);
32
+ write: (chunk, encoding, callback) => {
33
+ this._buffer += chunk;
34
+ if (this._buffer.length >= MIN_CHUNK_SIZE) this._pushBuffer();else if (this._flushTimer === null) this._armFlushTimer();
24
35
  callback && callback();
25
36
  },
26
37
  end: callback => {
38
+ this._pushBuffer();
27
39
  this.push(null);
28
40
  callback && callback();
29
41
  }
30
42
  }, options);
31
43
 
32
- // Implement Transform methods on top of writer
44
+ // Flush buffered output before serialization errors
45
+ let pendingDone = null;
46
+ const quadDone = error => {
47
+ const done = pendingDone;
48
+ pendingDone = null;
49
+ if (error) this._pushBuffer();
50
+ done(error);
51
+ };
33
52
  this._transform = (quad, encoding, done) => {
34
- writer.addQuad(quad, done);
53
+ pendingDone = done;
54
+ writer.addQuad(quad, quadDone);
35
55
  };
36
56
  this._flush = done => {
37
57
  writer.end(done);
38
58
  };
39
59
  }
40
60
 
61
+ // ### `_pushBuffer` flushes coalesced output to the stream queue
62
+ _pushBuffer() {
63
+ this._clearFlushTimer();
64
+ if (this._buffer !== '') {
65
+ this.push(this._buffer);
66
+ this._buffer = '';
67
+ }
68
+ }
69
+
70
+ // ### `_armFlushTimer` schedules a partial-chunk flush
71
+ _armFlushTimer() {
72
+ this._flushTimer = setTimeout(() => {
73
+ this._flushTimer = null;
74
+ this._pushBuffer();
75
+ }, this._flushDelay);
76
+ // Browser timers do not implement `unref`
77
+ this._flushTimer.unref && this._flushTimer.unref();
78
+ }
79
+
80
+ // ### `_clearFlushTimer` cancels a scheduled flush
81
+ _clearFlushTimer() {
82
+ if (this._flushTimer !== null) {
83
+ clearTimeout(this._flushTimer);
84
+ this._flushTimer = null;
85
+ }
86
+ }
87
+
88
+ // ### `_destroy` cancels a scheduled flush, so it cannot fire afterwards
89
+ _destroy(error, callback) {
90
+ this._clearFlushTimer();
91
+ super._destroy(error, callback);
92
+ }
93
+
41
94
  // ### Serializes a stream of quads
42
95
  import(stream) {
43
96
  stream.on('data', quad => {
@@ -47,6 +100,7 @@ class N3StreamWriter extends _readableStream.Transform {
47
100
  this.end();
48
101
  });
49
102
  stream.on('error', error => {
103
+ this._pushBuffer();
50
104
  this.emit('error', error);
51
105
  });
52
106
  stream.on('prefix', (prefix, iri) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n3",
3
- "version": "2.2.9",
3
+ "version": "2.2.11",
4
4
  "description": "Lightning fast, asynchronous, streaming Turtle / N3 / RDF library.",
5
5
  "author": "Ruben Verborgh <ruben.verborgh@gmail.com>",
6
6
  "keywords": [
package/src/N3Store.js CHANGED
@@ -6,6 +6,7 @@ import { isDefaultGraph } from './N3Util';
6
6
  import N3Writer from './N3Writer';
7
7
 
8
8
  const ITERATOR = Symbol('iter');
9
+ const SIZE = Symbol('size');
9
10
 
10
11
  function hasInIndex(index0, key0, key1, key2) {
11
12
  const index1 = index0 && index0[key0];
@@ -14,11 +15,18 @@ function hasInIndex(index0, key0, key1, key2) {
14
15
  }
15
16
 
16
17
  function merge(target, source, depth = 4) {
17
- if (depth === 0)
18
- return Object.assign(target, source);
19
-
20
- for (const key in source)
21
- target[key] = merge(target[key] || Object.create(null), source[key], depth - 1);
18
+ let size = target[SIZE] || 0;
19
+ for (const key in source) {
20
+ if (!(key in target)) {
21
+ size++;
22
+ target[key] = depth === 0 ? null : merge(Object.create(null), source[key], depth - 1);
23
+ }
24
+ else if (depth !== 0)
25
+ target[key] = merge(target[key], source[key], depth - 1);
26
+ }
27
+ // Depth 2 is the level of the `subjects`, `predicates`, and `objects` indexes.
28
+ if (depth <= 2)
29
+ target[SIZE] = size;
22
30
 
23
31
  return target;
24
32
  }
@@ -31,7 +39,10 @@ function merge(target, source, depth = 4) {
31
39
  * *not* be set as the value for an index.
32
40
  */
33
41
  function intersect(s1, s2, depth = 4) {
34
- let target = false;
42
+ let target = false, size = 0;
43
+
44
+ if (depth <= 2 && s2[SIZE] < s1[SIZE])
45
+ [s1, s2] = [s2, s1];
35
46
 
36
47
  for (const key in s1) {
37
48
  if (key in s2) {
@@ -39,6 +50,7 @@ function intersect(s1, s2, depth = 4) {
39
50
  if (intersection !== false) {
40
51
  target = target || Object.create(null);
41
52
  target[key] = intersection;
53
+ size++;
42
54
  }
43
55
  // Depth 3 is the 'subjects', 'predicates' and 'objects' keys.
44
56
  // If the 'subjects' index is empty, so will the 'predicates' and 'objects' index.
@@ -48,6 +60,10 @@ function intersect(s1, s2, depth = 4) {
48
60
  }
49
61
  }
50
62
 
63
+ // Depth 2 is the level of the `subjects`, `predicates`, and `objects` indexes.
64
+ if (depth <= 2 && target)
65
+ target[SIZE] = size;
66
+
51
67
  return target;
52
68
  }
53
69
 
@@ -59,7 +75,7 @@ function intersect(s1, s2, depth = 4) {
59
75
  * *not* be set as the value for an index.
60
76
  */
61
77
  function difference(s1, s2, depth = 4) {
62
- let target = false;
78
+ let target = false, size = 0;
63
79
 
64
80
  for (const key in s1) {
65
81
  // When the key is not in the index, then none of the triples defined by s1[key] are
@@ -67,12 +83,14 @@ function difference(s1, s2, depth = 4) {
67
83
  if (!(key in s2)) {
68
84
  target = target || Object.create(null);
69
85
  target[key] = depth === 0 ? null : merge({}, s1[key], depth - 1);
86
+ size++;
70
87
  }
71
88
  else if (depth !== 0) {
72
89
  const diff = difference(s1[key], s2[key], depth - 1);
73
90
  if (diff !== false) {
74
91
  target = target || Object.create(null);
75
92
  target[key] = diff;
93
+ size++;
76
94
  }
77
95
  // Depth 3 is the 'subjects', 'predicates' and 'objects' keys.
78
96
  // If the 'subjects' index is empty, so will the 'predicates' and 'objects' index.
@@ -82,6 +100,10 @@ function difference(s1, s2, depth = 4) {
82
100
  }
83
101
  }
84
102
 
103
+ // Depth 2 is the level of the `subjects`, `predicates`, and `objects` indexes.
104
+ if (depth <= 2 && target)
105
+ target[SIZE] = size;
106
+
85
107
  return target;
86
108
  }
87
109
 
@@ -200,7 +222,7 @@ export default class N3Store {
200
222
  for (const graphKey in graphs)
201
223
  for (const subjectKey in (subjects = graphs[graphKey].subjects))
202
224
  for (const predicateKey in (subject = subjects[subjectKey]))
203
- size += Object.keys(subject[predicateKey]).length;
225
+ size += subject[predicateKey][SIZE];
204
226
  return this._size = size;
205
227
  }
206
228
 
@@ -209,13 +231,23 @@ export default class N3Store {
209
231
  // ### `_addToIndex` adds a quad to a three-layered index.
210
232
  // Returns if the index has changed, if the entry did not already exist.
211
233
  _addToIndex(index0, key0, key1, key2) {
212
- // Create layers as necessary
213
- const index1 = index0[key0] || (index0[key0] = {});
214
- const index2 = index1[key1] || (index1[key1] = {});
234
+ // Create layers as necessary, maintaining their entry counters
235
+ let index1 = index0[key0];
236
+ if (!index1) {
237
+ index0[key0] = index1 = { [SIZE]: 0 };
238
+ index0[SIZE]++;
239
+ }
240
+ let index2 = index1[key1];
241
+ if (!index2) {
242
+ index1[key1] = index2 = { [SIZE]: 0 };
243
+ index1[SIZE]++;
244
+ }
215
245
  // Setting the key to _any_ value signals the presence of the quad
216
246
  const existed = key2 in index2;
217
- if (!existed)
247
+ if (!existed) {
218
248
  index2[key2] = null;
249
+ index2[SIZE]++;
250
+ }
219
251
  return !existed;
220
252
  }
221
253
 
@@ -225,11 +257,13 @@ export default class N3Store {
225
257
  const index1 = index0[key0], index2 = index1[key1];
226
258
  delete index2[key2];
227
259
 
228
- // Remove intermediary index layers if they are empty
229
- for (const key in index2) return;
260
+ // Remove intermediary index layers if they are empty,
261
+ // which the entry counters detect in constant time
262
+ if (--index2[SIZE] !== 0) return;
230
263
  delete index1[key1];
231
- for (const key in index1) return;
264
+ if (--index1[SIZE] !== 0) return;
232
265
  delete index0[key0];
266
+ index0[SIZE]--;
233
267
  }
234
268
 
235
269
  // ### `_findInIndex` finds a set of quads in a three-layered index.
@@ -332,7 +366,7 @@ export default class N3Store {
332
366
  // If a key is specified, count the quad if it exists
333
367
  if (key2) (key2 in index2) && count++;
334
368
  // Otherwise, count all quads
335
- else count += Object.keys(index2).length;
369
+ else count += index2[SIZE];
336
370
  }
337
371
  }
338
372
  }
@@ -384,7 +418,11 @@ export default class N3Store {
384
418
  let graphItem = this._graphs[graph];
385
419
  // Create the graph if it doesn't exist yet
386
420
  if (!graphItem) {
387
- graphItem = this._graphs[graph] = { subjects: {}, predicates: {}, objects: {} };
421
+ graphItem = this._graphs[graph] = {
422
+ subjects: { [SIZE]: 0 },
423
+ predicates: { [SIZE]: 0 },
424
+ objects: { [SIZE]: 0 },
425
+ };
388
426
  // Freezing a graph helps subsequent `add` performance,
389
427
  // and properties will never be modified anyway
390
428
  Object.freeze(graphItem);
@@ -469,8 +507,8 @@ export default class N3Store {
469
507
  if (this._size !== null) this._size--;
470
508
 
471
509
  // Remove the graph if it is empty
472
- for (subject in graphItem.subjects) return true;
473
- delete graphs[graph];
510
+ if (graphItem.subjects[SIZE] === 0)
511
+ delete graphs[graph];
474
512
  return true;
475
513
  }
476
514
 
@@ -1130,15 +1168,18 @@ function indexMatch(index, ids, depth = 0) {
1130
1168
  if (ind && !(ind in index))
1131
1169
  return false;
1132
1170
 
1133
- let target = false;
1171
+ let target = false, size = 0;
1134
1172
  for (const key in (ind ? { [ind]: index[ind] } : index)) {
1135
1173
  const result = depth === 2 ? null : indexMatch(index[key], ids, depth + 1);
1136
1174
 
1137
1175
  if (result !== false) {
1138
1176
  target = target || Object.create(null);
1139
1177
  target[key] = result;
1178
+ size++;
1140
1179
  }
1141
1180
  }
1181
+ if (target)
1182
+ target[SIZE] = size;
1142
1183
  return target;
1143
1184
  }
1144
1185
 
@@ -2,27 +2,89 @@
2
2
  import { Transform } from 'readable-stream';
3
3
  import N3Writer from './N3Writer';
4
4
 
5
+ const MIN_CHUNK_SIZE = 16 * 1024;
6
+ const DEFAULT_FLUSH_DELAY_MS = 20;
7
+
5
8
  // ## Constructor
6
9
  export default class N3StreamWriter extends Transform {
7
10
  constructor(options) {
8
11
  super({ encoding: 'utf8', writableObjectMode: true });
9
12
 
13
+ // Coalesce serialized fragments into larger stream chunks
14
+ this._buffer = '';
15
+
16
+ // Flush partial chunks after a bounded delay
17
+ this._flushTimer = null;
18
+ this._flushDelay = options && options.flushDelay !== undefined ?
19
+ options.flushDelay : DEFAULT_FLUSH_DELAY_MS;
20
+
10
21
  // Set up writer with a dummy stream object
11
22
  const writer = this._writer = new N3Writer({
12
- write: (quad, encoding, callback) => { this.push(quad); callback && callback(); },
13
- end: callback => { this.push(null); callback && callback(); },
23
+ write: (chunk, encoding, callback) => {
24
+ this._buffer += chunk;
25
+ if (this._buffer.length >= MIN_CHUNK_SIZE)
26
+ this._pushBuffer();
27
+ else if (this._flushTimer === null)
28
+ this._armFlushTimer();
29
+ callback && callback();
30
+ },
31
+ end: callback => { this._pushBuffer(); this.push(null); callback && callback(); },
14
32
  }, options);
15
33
 
16
- // Implement Transform methods on top of writer
17
- this._transform = (quad, encoding, done) => { writer.addQuad(quad, done); };
34
+ // Flush buffered output before serialization errors
35
+ let pendingDone = null;
36
+ const quadDone = error => {
37
+ const done = pendingDone;
38
+ pendingDone = null;
39
+ if (error)
40
+ this._pushBuffer();
41
+ done(error);
42
+ };
43
+ this._transform = (quad, encoding, done) => {
44
+ pendingDone = done;
45
+ writer.addQuad(quad, quadDone);
46
+ };
18
47
  this._flush = done => { writer.end(done); };
19
48
  }
20
49
 
50
+ // ### `_pushBuffer` flushes coalesced output to the stream queue
51
+ _pushBuffer() {
52
+ this._clearFlushTimer();
53
+ if (this._buffer !== '') {
54
+ this.push(this._buffer);
55
+ this._buffer = '';
56
+ }
57
+ }
58
+
59
+ // ### `_armFlushTimer` schedules a partial-chunk flush
60
+ _armFlushTimer() {
61
+ this._flushTimer = setTimeout(() => {
62
+ this._flushTimer = null;
63
+ this._pushBuffer();
64
+ }, this._flushDelay);
65
+ // Browser timers do not implement `unref`
66
+ this._flushTimer.unref && this._flushTimer.unref();
67
+ }
68
+
69
+ // ### `_clearFlushTimer` cancels a scheduled flush
70
+ _clearFlushTimer() {
71
+ if (this._flushTimer !== null) {
72
+ clearTimeout(this._flushTimer);
73
+ this._flushTimer = null;
74
+ }
75
+ }
76
+
77
+ // ### `_destroy` cancels a scheduled flush, so it cannot fire afterwards
78
+ _destroy(error, callback) {
79
+ this._clearFlushTimer();
80
+ super._destroy(error, callback);
81
+ }
82
+
21
83
  // ### Serializes a stream of quads
22
84
  import(stream) {
23
85
  stream.on('data', quad => { this.write(quad); });
24
86
  stream.on('end', () => { this.end(); });
25
- stream.on('error', error => { this.emit('error', error); });
87
+ stream.on('error', error => { this._pushBuffer(); this.emit('error', error); });
26
88
  stream.on('prefix', (prefix, iri) => { this._writer.addPrefix(prefix, iri); });
27
89
  return this;
28
90
  }