n3.js-messages 0.1.2 → 0.1.4

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/README.md CHANGED
@@ -108,8 +108,12 @@ const text = writeMessages(messages, { format: 'N-Quads' }); // synchronous, re
108
108
  message survives a round trip.
109
109
  - Each message starts from a clean serializer state, so consecutive messages in the same named graph are valid. Prefixes (and `baseIRI` with
110
110
  `writeBase: true`) are declared once; `addPrefix` declares more before the next message.
111
- - `addMessage` and `end` accept a callback or return a promise. They complete when the output stream has accepted the data, not when it was queued.
112
- - `MessageStreamWriter` is the `Transform` equivalent: messages in (object mode), text out.
111
+ - Quads can also be streamed within a message, with constant memory: `beginMessage()` (optional), `addQuad(quad)` (written immediately),
112
+ `endMessage()` (writes the delimiter and resets the serializer state; without an open message it writes an empty one). `end()` closes an open message.
113
+ Alternatively, tag quads as in rdf-writer-ts, `addQuad(quad, messageIndex)`: a change of index ends the current message and writes the empty messages in
114
+ between; a lower index is an error. `addMessage(quads)` is shorthand for begin, add all, end, and fails if a message is open.
115
+ - `addMessage`, `addQuad`, `beginMessage`, `endMessage` and `end` accept a callback or return a promise. They complete when the output stream has accepted the data, not when it was queued.
116
+ - `MessageStreamWriter` is the `Transform` equivalent: messages in (object mode), text out. It also accepts `{ quad, messageIndex }` objects.
113
117
 
114
118
  ## Blank node semantics
115
119
 
package/index.d.ts CHANGED
@@ -101,6 +101,16 @@ export class MessageWriter {
101
101
  constructor(outputStream?: MessageOutputStream | NodeJS.WritableStream | null, options?: MessageWriterOptions);
102
102
  addMessage(quads: RDF.BaseQuad[], done: (error: Error | null) => void): void;
103
103
  addMessage(quads: RDF.BaseQuad[]): Promise<void>;
104
+ /** Opens a message; optional, as `addQuad` opens one implicitly. */
105
+ beginMessage(done: (error: Error | null) => void): void;
106
+ beginMessage(): Promise<void>;
107
+ /** Writes a quad right away. With a 0-based `messageIndex`, a change of index ends the message and writes empty messages for gaps; a lower index is an error. */
108
+ addQuad(quad: RDF.BaseQuad, messageIndex: number | undefined, done: (error: Error | null) => void): void;
109
+ addQuad(quad: RDF.BaseQuad, done: (error: Error | null) => void): void;
110
+ addQuad(quad: RDF.BaseQuad, messageIndex?: number): Promise<void>;
111
+ /** Ends the open message (writes an empty message if none is open). */
112
+ endMessage(done: (error: Error | null) => void): void;
113
+ endMessage(): Promise<void>;
104
114
  addPrefix(prefix: string, iri: RDF.NamedNode | string, done: (error: Error | null) => void): void;
105
115
  addPrefix(prefix: string, iri: RDF.NamedNode | string): Promise<void>;
106
116
  /** Without an output stream, the result is the written string. */
@@ -9,7 +9,7 @@ var _MessageWriter = _interopRequireDefault(require("./MessageWriter.js"));
9
9
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
10
  // **MessageStreamWriter** is a Node.js stream that serializes RDF Messages.
11
11
  //
12
- // It consumes messages (arrays of quads) and produces text.
12
+ // It consumes messages (arrays of quads), or `{ quad, messageIndex }` objects, and produces text.
13
13
 
14
14
  class MessageStreamWriter extends _stream.Transform {
15
15
  constructor(options) {
@@ -26,7 +26,8 @@ class MessageStreamWriter extends _stream.Transform {
26
26
  }));
27
27
  }
28
28
  _transform(message, encoding, done) {
29
- this._writer.addMessage(message, done);
29
+ // A message (array of quads), or a quad of a message: `{ quad, messageIndex }`
30
+ if (Array.isArray(message)) this._writer.addMessage(message, done);else this._writer.addQuad(message.quad, message.messageIndex, done);
30
31
  }
31
32
  _flush(done) {
32
33
  this._writer.end(done);
@@ -40,6 +40,8 @@ class MessageWriter {
40
40
  this._error = null;
41
41
  this._started = false;
42
42
  this._ended = false;
43
+ this._open = false;
44
+ this._count = 0; // messages begun so far: the index of the next message
43
45
  this._declared = Object.assign({}, options.prefixes);
44
46
  this._newPrefixes = {};
45
47
  // The N3.js writer writes to this sink, which it never ends
@@ -53,8 +55,37 @@ class MessageWriter {
53
55
  // The callback (or returned promise) completes once the output is flushed.
54
56
  addMessage(quads, done) {
55
57
  return this._complete(done, () => {
56
- this._start();
57
- this._writeMessage(quads || []);
58
+ if (this._open) throw new Error('A message is already open');
59
+ this._begin();
60
+ for (const quad of quads || []) this._add(quad);
61
+ this._close();
62
+ });
63
+ }
64
+
65
+ // ### `beginMessage` opens a message. It is optional: the first `addQuad` opens one implicitly.
66
+ beginMessage(done) {
67
+ return this._complete(done, () => {
68
+ if (this._open) throw new Error('A message is already open');
69
+ this._begin();
70
+ });
71
+ }
72
+
73
+ // ### `addQuad` writes a quad into the open message right away (a message is opened if there is none).
74
+ // With a `messageIndex` (0-based), a change of index ends the current message and writes the empty
75
+ // messages in between, as rdf-writer-ts does; a lower index than the current one is an error.
76
+ addQuad(quad, messageIndex, done) {
77
+ if (typeof messageIndex === 'function') done = messageIndex, messageIndex = undefined;
78
+ return this._complete(done, () => {
79
+ if (messageIndex !== undefined && messageIndex !== null) this._moveTo(messageIndex);else if (!this._open) this._begin();
80
+ this._add(quad);
81
+ });
82
+ }
83
+
84
+ // ### `endMessage` ends the open message with a delimiter. Without an open message, it writes an empty one.
85
+ endMessage(done) {
86
+ return this._complete(done, () => {
87
+ if (!this._open) this._begin();
88
+ this._close();
58
89
  });
59
90
  }
60
91
 
@@ -72,6 +103,7 @@ class MessageWriter {
72
103
  // Ending twice is harmless
73
104
  if (this._ended) return typeof done === 'function' ? done(null) : Promise.resolve();
74
105
  this._start();
106
+ if (this._open) this._close();
75
107
  if (typeof done === 'function') return this._finish(done);
76
108
  return new Promise((resolve, reject) => this._finish((error, result) => error ? reject(error) : resolve(result)));
77
109
  }
@@ -150,16 +182,40 @@ class MessageWriter {
150
182
  });
151
183
  }
152
184
  }
153
- _writeMessage(quads) {
185
+
186
+ // Opens a message: declares the prefixes that were added since the previous one
187
+ _begin() {
188
+ this._start();
154
189
  const writer = this._writer;
155
190
  for (const prefix of Object.keys(this._newPrefixes)) {
156
191
  writer.addPrefix(prefix, this._newPrefixes[prefix]);
157
192
  this._declared[prefix] = this._newPrefixes[prefix];
158
193
  }
159
194
  this._newPrefixes = {};
195
+ this._open = true;
196
+ this._count++;
197
+ }
198
+ _add(quad) {
160
199
  const record = error => error && (this._error = this._error || error);
161
- for (const quad of quads) writer.addQuad(quad.subject, quad.predicate, quad.object, quad.graph, record);
162
- writer.addDelimiter();
200
+ this._writer.addQuad(quad.subject, quad.predicate, quad.object, quad.graph, record);
201
+ }
202
+ _close() {
203
+ this._open = false;
204
+ this._writer.addDelimiter();
205
+ }
206
+
207
+ // Makes message `index` the open message, writing empty messages for the gap
208
+ _moveTo(index) {
209
+ const current = this._count - 1;
210
+ if (!Number.isInteger(index) || index < 0) throw new RangeError(`Invalid message index: ${index}`);
211
+ if (this._open && index === current) return;
212
+ if (index <= current) throw new RangeError(`Message index ${index} is lower than the current message index ${current}`);
213
+ if (this._open) this._close();
214
+ while (this._count < index) {
215
+ this._begin();
216
+ this._close();
217
+ }
218
+ this._begin();
163
219
  }
164
220
  }
165
221
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n3.js-messages",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "RDF Messages (message-delimited Turtle, TriG, N-Triples and N-Quads) for N3.js: message-aware parser, streaming parser and writer.",
5
5
  "author": "Pieter Colpaert",
6
6
  "license": "MIT",
@@ -1,6 +1,6 @@
1
1
  // **MessageStreamWriter** is a Node.js stream that serializes RDF Messages.
2
2
  //
3
- // It consumes messages (arrays of quads) and produces text.
3
+ // It consumes messages (arrays of quads), or `{ quad, messageIndex }` objects, and produces text.
4
4
  import { Transform } from 'stream';
5
5
  import MessageWriter from './MessageWriter.js';
6
6
 
@@ -13,7 +13,9 @@ export default class MessageStreamWriter extends Transform {
13
13
  }
14
14
 
15
15
  _transform(message, encoding, done) {
16
- this._writer.addMessage(message, done);
16
+ // A message (array of quads), or a quad of a message: `{ quad, messageIndex }`
17
+ if (Array.isArray(message)) this._writer.addMessage(message, done);
18
+ else this._writer.addQuad(message.quad, message.messageIndex, done);
17
19
  }
18
20
 
19
21
  _flush(done) {
@@ -37,6 +37,8 @@ export default class MessageWriter {
37
37
  this._error = null;
38
38
  this._started = false;
39
39
  this._ended = false;
40
+ this._open = false;
41
+ this._count = 0; // messages begun so far: the index of the next message
40
42
  this._declared = Object.assign({}, options.prefixes);
41
43
  this._newPrefixes = {};
42
44
  // The N3.js writer writes to this sink, which it never ends
@@ -48,8 +50,38 @@ export default class MessageWriter {
48
50
  // The callback (or returned promise) completes once the output is flushed.
49
51
  addMessage(quads, done) {
50
52
  return this._complete(done, () => {
51
- this._start();
52
- this._writeMessage(quads || []);
53
+ if (this._open) throw new Error('A message is already open');
54
+ this._begin();
55
+ for (const quad of quads || []) this._add(quad);
56
+ this._close();
57
+ });
58
+ }
59
+
60
+ // ### `beginMessage` opens a message. It is optional: the first `addQuad` opens one implicitly.
61
+ beginMessage(done) {
62
+ return this._complete(done, () => {
63
+ if (this._open) throw new Error('A message is already open');
64
+ this._begin();
65
+ });
66
+ }
67
+
68
+ // ### `addQuad` writes a quad into the open message right away (a message is opened if there is none).
69
+ // With a `messageIndex` (0-based), a change of index ends the current message and writes the empty
70
+ // messages in between, as rdf-writer-ts does; a lower index than the current one is an error.
71
+ addQuad(quad, messageIndex, done) {
72
+ if (typeof messageIndex === 'function') done = messageIndex, messageIndex = undefined;
73
+ return this._complete(done, () => {
74
+ if (messageIndex !== undefined && messageIndex !== null) this._moveTo(messageIndex);
75
+ else if (!this._open) this._begin();
76
+ this._add(quad);
77
+ });
78
+ }
79
+
80
+ // ### `endMessage` ends the open message with a delimiter. Without an open message, it writes an empty one.
81
+ endMessage(done) {
82
+ return this._complete(done, () => {
83
+ if (!this._open) this._begin();
84
+ this._close();
53
85
  });
54
86
  }
55
87
 
@@ -69,6 +101,8 @@ export default class MessageWriter {
69
101
  if (this._ended)
70
102
  return typeof done === 'function' ? done(null) : Promise.resolve();
71
103
  this._start();
104
+ if (this._open)
105
+ this._close();
72
106
  if (typeof done === 'function')
73
107
  return this._finish(done);
74
108
  return new Promise((resolve, reject) => this._finish((error, result) => error ? reject(error) : resolve(result)));
@@ -165,17 +199,39 @@ export default class MessageWriter {
165
199
  }
166
200
  }
167
201
 
168
- _writeMessage(quads) {
202
+ // Opens a message: declares the prefixes that were added since the previous one
203
+ _begin() {
204
+ this._start();
169
205
  const writer = this._writer;
170
206
  for (const prefix of Object.keys(this._newPrefixes)) {
171
207
  writer.addPrefix(prefix, this._newPrefixes[prefix]);
172
208
  this._declared[prefix] = this._newPrefixes[prefix];
173
209
  }
174
210
  this._newPrefixes = {};
211
+ this._open = true;
212
+ this._count++;
213
+ }
214
+ _add(quad) {
175
215
  const record = error => error && (this._error = this._error || error);
176
- for (const quad of quads)
177
- writer.addQuad(quad.subject, quad.predicate, quad.object, quad.graph, record);
178
- writer.addDelimiter();
216
+ this._writer.addQuad(quad.subject, quad.predicate, quad.object, quad.graph, record);
217
+ }
218
+ _close() {
219
+ this._open = false;
220
+ this._writer.addDelimiter();
221
+ }
222
+
223
+ // Makes message `index` the open message, writing empty messages for the gap
224
+ _moveTo(index) {
225
+ const current = this._count - 1;
226
+ if (!Number.isInteger(index) || index < 0) throw new RangeError(`Invalid message index: ${index}`);
227
+ if (this._open && index === current) return;
228
+ if (index <= current) throw new RangeError(`Message index ${index} is lower than the current message index ${current}`);
229
+ if (this._open) this._close();
230
+ while (this._count < index) {
231
+ this._begin();
232
+ this._close();
233
+ }
234
+ this._begin();
179
235
  }
180
236
  }
181
237