@jarenjs/josl 0.49.2 → 0.66.1
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/FORMAT.md +42 -5
- package/README.md +95 -6
- package/dist/types/csv-machine.d.ts +9 -0
- package/dist/types/csv-stream.d.ts +27 -3
- package/dist/types/csv.d.ts +24 -3
- package/dist/types/index.d.ts +4 -3
- package/dist/types/jsonx-stream.d.ts +9 -0
- package/dist/types/limits.d.ts +41 -0
- package/dist/types/machine.d.ts +16 -0
- package/dist/types/pull.d.ts +14 -0
- package/dist/types/stream.d.ts +20 -0
- package/dist/types/util.d.ts +12 -0
- package/dist/types/write.d.ts +24 -2
- package/package.json +2 -2
- package/src/csv-machine.js +75 -4
- package/src/csv-stream.js +83 -22
- package/src/csv.js +65 -36
- package/src/index.js +4 -2
- package/src/jsonx-stream.js +70 -6
- package/src/limits.js +96 -0
- package/src/machine.js +112 -13
- package/src/pull.js +40 -0
- package/src/stream.js +58 -0
- package/src/util.js +15 -0
- package/src/write.js +78 -13
package/src/write.js
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
formatSection,
|
|
18
18
|
isPlainTable,
|
|
19
19
|
} from './stringify.js';
|
|
20
|
+
import { closeIterator, abortedError } from './pull.js';
|
|
20
21
|
|
|
21
22
|
class JoslStreamWriter {
|
|
22
23
|
constructor(options = {}) {
|
|
@@ -26,7 +27,14 @@ class JoslStreamWriter {
|
|
|
26
27
|
onRegExp: options.onRegExp,
|
|
27
28
|
};
|
|
28
29
|
this.onChunk = options.onChunk ?? null;
|
|
30
|
+
// `buffer: false` hands every chunk to `onChunk` and keeps none: the
|
|
31
|
+
// caller chose its sink, so the writer retains no second copy of the
|
|
32
|
+
// document; `text()`/`end()` then answer '' and say so
|
|
33
|
+
this.buffered = options.buffer !== false;
|
|
34
|
+
if (!this.buffered && this.onChunk === null)
|
|
35
|
+
throw new JoslStringifyError('buffer: false needs an onChunk sink to deliver the chunks to');
|
|
29
36
|
this.chunks = [];
|
|
37
|
+
this.emitted = false;
|
|
30
38
|
this.ended = false;
|
|
31
39
|
this.rootIsArray = false;
|
|
32
40
|
this.rootHasPairs = false;
|
|
@@ -37,7 +45,9 @@ class JoslStreamWriter {
|
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
emit(chunk) {
|
|
40
|
-
this.
|
|
48
|
+
this.emitted = true;
|
|
49
|
+
if (this.buffered)
|
|
50
|
+
this.chunks.push(chunk);
|
|
41
51
|
if (this.onChunk !== null)
|
|
42
52
|
this.onChunk(chunk);
|
|
43
53
|
}
|
|
@@ -48,7 +58,7 @@ class JoslStreamWriter {
|
|
|
48
58
|
}
|
|
49
59
|
|
|
50
60
|
blank() {
|
|
51
|
-
if (this.
|
|
61
|
+
if (this.emitted)
|
|
52
62
|
this.emit('\n');
|
|
53
63
|
}
|
|
54
64
|
|
|
@@ -159,7 +169,8 @@ class JoslStreamWriter {
|
|
|
159
169
|
}
|
|
160
170
|
|
|
161
171
|
/**
|
|
162
|
-
* The document text emitted so far
|
|
172
|
+
* The document text emitted so far; `''` under `buffer: false`, whose
|
|
173
|
+
* chunks went to the sink and nowhere else.
|
|
163
174
|
* @returns {string} Concatenated chunks
|
|
164
175
|
*/
|
|
165
176
|
text() {
|
|
@@ -179,12 +190,15 @@ class JoslStreamWriter {
|
|
|
179
190
|
/**
|
|
180
191
|
* Create a streaming JOSL/TOML writer - the write-side mirror of
|
|
181
192
|
* `createStreamReader`. Chunks are delivered through `onChunk` as they
|
|
182
|
-
* are produced and also accumulate for `text()` / `end()
|
|
193
|
+
* are produced and also accumulate for `text()` / `end()` — unless
|
|
194
|
+
* `buffer: false`, which keeps no copy: the sink is the only holder.
|
|
183
195
|
* @param {object} [options] - Writer options
|
|
184
196
|
* @param {'josl'|'toml'} [options.mode] - 'toml' emits strict TOML 1.0
|
|
185
197
|
* @param {'error'|'omit'} [options.onNull] - See `stringifyJosl`
|
|
186
198
|
* @param {'error'|'string'} [options.onRegExp] - See `stringifyJosl`
|
|
187
199
|
* @param {(chunk: string) => void} [options.onChunk] - Chunk sink
|
|
200
|
+
* @param {boolean} [options.buffer] - `false` retains no emitted text
|
|
201
|
+
* (needs `onChunk`); `text()` and `end()` then answer `''`
|
|
188
202
|
* @returns {JoslStreamWriter} The writer
|
|
189
203
|
*/
|
|
190
204
|
export function createStreamWriter(options = undefined) {
|
|
@@ -203,15 +217,8 @@ export function* stringifyJoslChunks(value, options = {}) {
|
|
|
203
217
|
if (Array.isArray(value)) {
|
|
204
218
|
if (options.mode === 'toml')
|
|
205
219
|
throw new JoslStringifyError('a TOML root must be a table; root arrays are a JOSL extension');
|
|
206
|
-
for (let i = 0; i < value.length; ++i)
|
|
207
|
-
|
|
208
|
-
throw new JoslStringifyError('root array elements must be tables', [i]);
|
|
209
|
-
const body = formatSection(value[i], options);
|
|
210
|
-
// keep byte-identical with stringifyJosl: a body that opens with a
|
|
211
|
-
// section header gets a blank line after the [[]] header
|
|
212
|
-
yield (i === 0 ? '' : '\n') + '[[]]\n'
|
|
213
|
-
+ (body.startsWith('[') ? '\n' : '') + body;
|
|
214
|
-
}
|
|
220
|
+
for (let i = 0; i < value.length; ++i)
|
|
221
|
+
yield rootItemChunk(value[i], i, options);
|
|
215
222
|
return;
|
|
216
223
|
}
|
|
217
224
|
if (!isPlainTable(value))
|
|
@@ -221,6 +228,64 @@ export function* stringifyJoslChunks(value, options = {}) {
|
|
|
221
228
|
yield text;
|
|
222
229
|
}
|
|
223
230
|
|
|
231
|
+
/**
|
|
232
|
+
* One `[[]]` record as a chunk — the text both `stringifyJoslChunks` and
|
|
233
|
+
* `stringifyJoslStream` yield for record `index`, so the two are
|
|
234
|
+
* byte-identical by construction (and identical to `stringifyJosl`: a
|
|
235
|
+
* body that opens with a section header gets a blank line after the
|
|
236
|
+
* `[[]]` header).
|
|
237
|
+
* @param {*} record - A plain table
|
|
238
|
+
* @param {number} index - The record's position in the root array
|
|
239
|
+
* @param {object} options - Writer options
|
|
240
|
+
* @returns {string} The chunk
|
|
241
|
+
* @throws {JoslStringifyError} When the record is not a table
|
|
242
|
+
*/
|
|
243
|
+
function rootItemChunk(record, index, options) {
|
|
244
|
+
if (!isPlainTable(record))
|
|
245
|
+
throw new JoslStringifyError('root array elements must be tables', [index]);
|
|
246
|
+
const body = formatSection(record, options);
|
|
247
|
+
return (index === 0 ? '' : '\n') + '[[]]\n' + (body.startsWith('[') ? '\n' : '') + body;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Serialize an async iterable of table records as an async iterable of
|
|
252
|
+
* `[[]]` chunks — the pull form of `stringifyJoslChunks` over a root
|
|
253
|
+
* array, byte-identical to it for the same records. Pull is the
|
|
254
|
+
* backpressure: the next record is requested only when the consumer
|
|
255
|
+
* asks for the next chunk. `options.signal` aborts between pulls (the
|
|
256
|
+
* rejection is the signal's reason); an abort, a consumer that stops
|
|
257
|
+
* early or a throw closes the record source exactly once.
|
|
258
|
+
* @param {AsyncIterable<object>|Iterable<object>} records - Table records
|
|
259
|
+
* @param {object} [options] - Writer options; see `stringifyJosl`, plus `signal`
|
|
260
|
+
* @yields {string} One `[[]]` record per chunk
|
|
261
|
+
* @example
|
|
262
|
+
* response.body = stringifyJoslStream(store.collection('rows').query(doc), { signal });
|
|
263
|
+
*/
|
|
264
|
+
export async function* stringifyJoslStream(records, options = {}) {
|
|
265
|
+
if (options.mode === 'toml')
|
|
266
|
+
throw new JoslStringifyError('a TOML root must be a table; root arrays are a JOSL extension');
|
|
267
|
+
const signal = options.signal ?? null;
|
|
268
|
+
const iterator = records[Symbol.asyncIterator]?.() ?? records[Symbol.iterator]();
|
|
269
|
+
let index = 0;
|
|
270
|
+
let finished = false;
|
|
271
|
+
try {
|
|
272
|
+
for (;;) {
|
|
273
|
+
if (signal !== null && signal.aborted)
|
|
274
|
+
throw abortedError(signal);
|
|
275
|
+
const step = await iterator.next();
|
|
276
|
+
if (step.done) {
|
|
277
|
+
finished = true;
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
yield rootItemChunk(step.value, index++, options);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
finally {
|
|
284
|
+
if (!finished)
|
|
285
|
+
await closeIterator(iterator);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
224
289
|
export { JoslStringifyError } from './errors.js';
|
|
225
290
|
|
|
226
291
|
//#endregion
|