@jarenjs/josl 0.56.0 → 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 +93 -4
- 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/FORMAT.md
CHANGED
|
@@ -136,9 +136,31 @@ visits leaves bottom-up, only after the full text has arrived, and never
|
|
|
136
136
|
tells you where you are.
|
|
137
137
|
|
|
138
138
|
The write side mirrors this: `createStreamWriter` emits text chunks
|
|
139
|
-
event by event (`pair`, `table`, `tableArray`, `rootItem`
|
|
140
|
-
`
|
|
141
|
-
|
|
139
|
+
event by event (`pair`, `table`, `tableArray`, `rootItem`; `buffer:
|
|
140
|
+
false` retains none of them), `stringifyJoslChunks` streams an existing
|
|
141
|
+
value one `[[]]` record at a time, and `stringifyJoslStream` pulls
|
|
142
|
+
records from an async source one per chunk — all byte-identical to
|
|
143
|
+
`stringifyJosl` output. `iterateJoslStream` is the reading twin: a
|
|
144
|
+
`[[]]` root array as a pull source of records, each detached from the
|
|
145
|
+
root as the next header completes it; a table root is refused, being one
|
|
146
|
+
retained value rather than a record stream.
|
|
147
|
+
|
|
148
|
+
**Limits.** A reader refuses a document by size only when asked. Every
|
|
149
|
+
limit defaults to `Infinity`, counts UTF-8 bytes, and is judged while
|
|
150
|
+
the text is still in cutter, token or container state; a crossing is a
|
|
151
|
+
`JoslLimitError` with a stable code — never a repair.
|
|
152
|
+
|
|
153
|
+
| reader | option | code |
|
|
154
|
+
| --- | --- | --- |
|
|
155
|
+
| JOSL / TOML | `maxTotalBytes` | `JOSL2001` |
|
|
156
|
+
| JOSL / TOML | `maxRecordBytes` — one logical line | `JOSL2002` |
|
|
157
|
+
| JOSL / TOML | `maxTokenBytes` — a string or key as written, quotes included | `JOSL2003` |
|
|
158
|
+
| JOSL / TOML | `maxDepth` — inline nesting and header path depth | `JOSL2004` |
|
|
159
|
+
| JOSL / TOML | `maxRetainedValues` — values the root holds, starting over per detached `[[]]` item | `JOSL2005` |
|
|
160
|
+
| JSONX / JSON (stream reader) | `maxTotalBytes` | `JSONX2001` |
|
|
161
|
+
| JSONX / JSON (stream reader) | `maxTokenBytes` — a string, key, number or regexp as written | `JSONX2002` |
|
|
162
|
+
| JSONX / JSON (stream reader) | `maxDepth` | `JSONX2003` |
|
|
163
|
+
| JSONX / JSON (stream reader) | `maxRetainedValues` — linked values only; a detached subtree never counts | `JSONX2004` |
|
|
142
164
|
|
|
143
165
|
## JSONX
|
|
144
166
|
|
|
@@ -206,13 +228,28 @@ information that cannot be recovered downstream:
|
|
|
206
228
|
did not carry it, `''` would claim it carried nothing.
|
|
207
229
|
- `typed: true` promotes an integer beyond 2^53 to bigint rather than
|
|
208
230
|
rounding, reads unambiguous ISO-8601 as the value classes above, and
|
|
209
|
-
leaves
|
|
231
|
+
leaves numbers with a leading zero a string. Offset date-times use
|
|
232
|
+
the same instant conversion as JOSL, preserving four-digit years and
|
|
233
|
+
truncating precision beyond milliseconds; invalid offsets stay text.
|
|
210
234
|
- A BOM is stripped without comment; it is an encoding mark, not data.
|
|
211
235
|
|
|
212
236
|
**Writing** quotes a field only when it contains the delimiter, the quote
|
|
213
237
|
character, a newline, or edge whitespace a lenient reader might trim. The
|
|
214
238
|
default terminator is CRLF, per RFC 4180 §2.1 and what spreadsheet
|
|
215
|
-
software expects.
|
|
239
|
+
software expects. `stringifyCsv`, `stringifyCsvChunks`, the stream writer
|
|
240
|
+
and the pull form `stringifyCsvStream` share one row formatter and are
|
|
241
|
+
byte-identical for the same records; the pull form requests a record only
|
|
242
|
+
when its consumer asks for the next line.
|
|
243
|
+
|
|
244
|
+
**Limits.** Optional, `Infinity` by default, in UTF-8 bytes, judged before
|
|
245
|
+
the text is kept; a crossing is a `JoslLimitError`, never a repair:
|
|
246
|
+
|
|
247
|
+
| option | code |
|
|
248
|
+
| --- | --- |
|
|
249
|
+
| `maxTotalBytes` | `CSV2001` |
|
|
250
|
+
| `maxRecordBytes` — one record, its terminator included | `CSV2002` |
|
|
251
|
+
| `maxFieldBytes` — one field as written, quotes included | `CSV2003` |
|
|
252
|
+
| `maxColumns` | `CSV2004` |
|
|
216
253
|
|
|
217
254
|
## Compliance notes
|
|
218
255
|
|
package/README.md
CHANGED
|
@@ -129,6 +129,49 @@ records went past. The pattern matches an exact path, not a prefix, so a
|
|
|
129
129
|
feature's own rings are not separately detached: they belong to their
|
|
130
130
|
feature and are freed with it.
|
|
131
131
|
|
|
132
|
+
The JOSL twin is `iterateJoslStream`: a `[[]]` root array read as a pull
|
|
133
|
+
source of records, each detached from the root the moment the next
|
|
134
|
+
`[[]]` header (or the end) completes it, so the reader holds at most the
|
|
135
|
+
record in progress. A table root is one retained value, not a record
|
|
136
|
+
stream, and is refused by name.
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
import { iterateJoslStream } from '@jarenjs/josl/stream';
|
|
140
|
+
|
|
141
|
+
for await (const record of iterateJoslStream(response.body, { signal }))
|
|
142
|
+
await save(record); // the source is closed exactly once on an abort or an early return
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Hostile input
|
|
146
|
+
|
|
147
|
+
Every reader refuses a document by size only when asked: the limits
|
|
148
|
+
default to `Infinity`, count **UTF-8 bytes** (never code units), and are
|
|
149
|
+
checked while the offending text is still in cutter, token or container
|
|
150
|
+
state — before a concatenation or a link could cross them. A crossing
|
|
151
|
+
throws `JoslLimitError` with a stable code and the limit; it is never a
|
|
152
|
+
repair, because a document that is too large is not damaged.
|
|
153
|
+
|
|
154
|
+
| reader | option | code |
|
|
155
|
+
| --- | --- | --- |
|
|
156
|
+
| CSV | `maxTotalBytes` | `CSV2001` |
|
|
157
|
+
| CSV | `maxRecordBytes` (one record, its terminator included) | `CSV2002` |
|
|
158
|
+
| CSV | `maxFieldBytes` (one field as written, quotes included) | `CSV2003` |
|
|
159
|
+
| CSV | `maxColumns` | `CSV2004` |
|
|
160
|
+
| JOSL / TOML | `maxTotalBytes` | `JOSL2001` |
|
|
161
|
+
| JOSL / TOML | `maxRecordBytes` (one logical line) | `JOSL2002` |
|
|
162
|
+
| JOSL / TOML | `maxTokenBytes` (a string or key as written, quotes included) | `JOSL2003` |
|
|
163
|
+
| JOSL / TOML | `maxDepth` (inline nesting, header path depth) | `JOSL2004` |
|
|
164
|
+
| JOSL / TOML | `maxRetainedValues` (values the root holds; starts over per detached `[[]]` item) | `JOSL2005` |
|
|
165
|
+
| JSONX / JSON stream | `maxTotalBytes` | `JSONX2001` |
|
|
166
|
+
| JSONX / JSON stream | `maxTokenBytes` (a string, key, number or regexp as written) | `JSONX2002` |
|
|
167
|
+
| JSONX / JSON stream | `maxDepth` | `JSONX2003` |
|
|
168
|
+
| JSONX / JSON stream | `maxRetainedValues` (linked values only — a detached subtree is never counted) | `JSONX2004` |
|
|
169
|
+
|
|
170
|
+
The CSV and JOSL limits guard the whole-document parsers too, which run
|
|
171
|
+
the same machines; the JSONX limits are the stream reader's, which is
|
|
172
|
+
where a document arrives a chunk at a time. `CSV_LIMIT_CODES`,
|
|
173
|
+
`JOSL_LIMIT_CODES` and `JSONX_LIMIT_CODES` are the tables as data.
|
|
174
|
+
|
|
132
175
|
Measured on a synthetic OpenStreetMap-shaped extract (`node --expose-gc
|
|
133
176
|
benchmark/jsonx-stream.js`), reading 20 000 features of 40 vertices each:
|
|
134
177
|
|
|
@@ -212,23 +255,33 @@ The write-side mirror of the reader — build a document event by event and
|
|
|
212
255
|
ship each chunk as it is produced:
|
|
213
256
|
|
|
214
257
|
```js
|
|
215
|
-
import { createStreamWriter, stringifyJoslChunks } from '@jarenjs/josl/write';
|
|
258
|
+
import { createStreamWriter, stringifyJoslChunks, stringifyJoslStream } from '@jarenjs/josl/write';
|
|
216
259
|
|
|
217
|
-
const w = createStreamWriter({ onChunk: (c) => response.write(c) });
|
|
260
|
+
const w = createStreamWriter({ onChunk: (c) => response.write(c), buffer: false });
|
|
218
261
|
w.pair('title', 'run 42')
|
|
219
262
|
.table('server')
|
|
220
263
|
.pair('host', 'localhost');
|
|
221
264
|
// root-array documents: w.rootItem(record) per completed record
|
|
222
|
-
|
|
265
|
+
w.end(); // '' — buffer: false retains nothing; the sink has it
|
|
223
266
|
|
|
224
267
|
// or stream an existing value, one chunk per [[]] record:
|
|
225
268
|
for (const chunk of stringifyJoslChunks(records))
|
|
226
269
|
response.write(chunk);
|
|
270
|
+
|
|
271
|
+
// or pull records from an async source — a database cursor — one per chunk:
|
|
272
|
+
response.body = stringifyJoslStream(store.collection('rows').query(doc), { signal });
|
|
227
273
|
```
|
|
228
274
|
|
|
229
275
|
The writer validates what the reader would reject (duplicate keys and
|
|
230
276
|
headers, root table/array mixing, TOML downleveling) and shares its
|
|
231
277
|
serialization with `stringifyJosl`, so both produce identical text.
|
|
278
|
+
`stringifyJoslStream` is the pull form: the next record is requested only
|
|
279
|
+
when the consumer asks for the next chunk, so a cursor behind it never
|
|
280
|
+
runs ahead of the socket in front of it; its chunks are byte-identical to
|
|
281
|
+
`stringifyJoslChunks` for the same records, and an abort (`signal`), a
|
|
282
|
+
consumer that stops early or a throw closes the record source exactly
|
|
283
|
+
once. `buffer: false` (with an `onChunk` sink) keeps the event writer from
|
|
284
|
+
retaining a second copy of the document it streams.
|
|
232
285
|
|
|
233
286
|
## Editing a document (CST)
|
|
234
287
|
|
|
@@ -275,6 +328,30 @@ stringifyJsonx(v, { mode: 'json' }); // delegates to JSON.stringify
|
|
|
275
328
|
|
|
276
329
|
## Exports
|
|
277
330
|
|
|
331
|
+
Every subpath a consumer can import, derived from the manifest by
|
|
332
|
+
`npm run docs:derive` (`npm run docs:check` fails when the two drift):
|
|
333
|
+
|
|
334
|
+
<!--fact:exports.josl-->
|
|
335
|
+
| Import | Kind | Declarations |
|
|
336
|
+
|---|---|---|
|
|
337
|
+
| `@jarenjs/josl` | JavaScript | declared |
|
|
338
|
+
| `@jarenjs/josl/parse` | JavaScript | declared |
|
|
339
|
+
| `@jarenjs/josl/cst` | JavaScript | declared |
|
|
340
|
+
| `@jarenjs/josl/gbnf` | JavaScript | declared |
|
|
341
|
+
| `@jarenjs/josl/stream` | JavaScript | declared |
|
|
342
|
+
| `@jarenjs/josl/stringify` | JavaScript | declared |
|
|
343
|
+
| `@jarenjs/josl/write` | JavaScript | declared |
|
|
344
|
+
| `@jarenjs/josl/jsonx` | JavaScript | declared |
|
|
345
|
+
| `@jarenjs/josl/jsonx-stream` | JavaScript | declared |
|
|
346
|
+
| `@jarenjs/josl/csv` | JavaScript | declared |
|
|
347
|
+
| `@jarenjs/josl/csv-stream` | JavaScript | declared |
|
|
348
|
+
| `@jarenjs/josl/values` | JavaScript | declared |
|
|
349
|
+
| `@jarenjs/josl/schemas/jaren-josl-data.schema.json` | schema | — |
|
|
350
|
+
| `@jarenjs/josl/package.json` | metadata | — |
|
|
351
|
+
<!--/fact-->
|
|
352
|
+
|
|
353
|
+
What each subpath carries:
|
|
354
|
+
|
|
278
355
|
| Subpath | What |
|
|
279
356
|
| --- | --- |
|
|
280
357
|
| `@jarenjs/josl/parse` | `parseJosl`, `parseToml` |
|
|
@@ -304,10 +381,22 @@ parseCsv('a,b\n1,2'); // [['a','b'], ['1','2']]
|
|
|
304
381
|
parseCsv('a,b\n1,2', { headers: true }); // [{ a: '1', b: '2' }]
|
|
305
382
|
|
|
306
383
|
// rows as they complete, without ever holding the table
|
|
307
|
-
for await (const row of iterateCsvStream(response.body, { headers: true }))
|
|
384
|
+
for await (const row of iterateCsvStream(response.body, { headers: true, signal }))
|
|
308
385
|
await save(row);
|
|
386
|
+
|
|
387
|
+
// and the other way: pull records from a cursor into CSV lines, header once
|
|
388
|
+
response.body = stringifyCsvStream(store.collection('rows').query(doc), { signal });
|
|
309
389
|
```
|
|
310
390
|
|
|
391
|
+
`stringifyCsvStream` is byte-identical to `stringifyCsvChunks` and
|
|
392
|
+
`stringifyCsv` for the same records and pulls one record per line, so the
|
|
393
|
+
source is never asked for a row the consumer has not asked for; `signal`
|
|
394
|
+
aborts between pulls, and an abort, an early return or a throw closes the
|
|
395
|
+
source exactly once. The reader's `signal` does the same for
|
|
396
|
+
`iterateCsvStream`. The hostile-input limits (`maxTotalBytes`,
|
|
397
|
+
`maxRecordBytes`, `maxFieldBytes`, `maxColumns`, the `CSV2xxx` codes) are
|
|
398
|
+
listed under Streaming.
|
|
399
|
+
|
|
311
400
|
**Strict by default.** Anything RFC 4180 forbids throws a
|
|
312
401
|
`CsvSyntaxError` carrying a `CSV1xxx` code, a line and a column — the
|
|
313
402
|
same machine-repairable error shape the JOSL parser uses.
|
|
@@ -39,6 +39,12 @@ export declare class CsvMachine {
|
|
|
39
39
|
skipEmptyLines: boolean;
|
|
40
40
|
onEvent: any;
|
|
41
41
|
onRepair: any;
|
|
42
|
+
maxTotalBytes: number;
|
|
43
|
+
maxRecordBytes: number;
|
|
44
|
+
maxFieldBytes: number;
|
|
45
|
+
maxColumns: number;
|
|
46
|
+
limited: boolean;
|
|
47
|
+
totalBytes: number;
|
|
42
48
|
wantHeader: boolean;
|
|
43
49
|
headerFields: any;
|
|
44
50
|
objectRows: boolean;
|
|
@@ -69,6 +75,8 @@ export declare class CsvMachine {
|
|
|
69
75
|
* @returns {this} The machine, for chaining
|
|
70
76
|
*/
|
|
71
77
|
feed(chunk: string): this;
|
|
78
|
+
count(text: any): void;
|
|
79
|
+
endsRecordWithin(text: any): boolean;
|
|
72
80
|
/**
|
|
73
81
|
* Finish the document, flushing any pending record.
|
|
74
82
|
* @returns {Array} The completed rows
|
|
@@ -94,6 +102,7 @@ export declare class CsvMachine {
|
|
|
94
102
|
scan(): void;
|
|
95
103
|
compact(start: any): void;
|
|
96
104
|
readSpan(text: any, pos: any, end: any): any;
|
|
105
|
+
admitCell(cells: any, text: any, start: any, stop: any): void;
|
|
97
106
|
parseRecord(text: any, pos: any, end: any, cells: any): any;
|
|
98
107
|
parsePlain(text: any, pos: any, end: any, cells: any): any;
|
|
99
108
|
parseQuoted(text: any, pos: any, end: any, cells: any): any;
|
|
@@ -48,6 +48,23 @@ export declare function parseCsvStream(chunks: AsyncIterable<string> | Iterable<
|
|
|
48
48
|
* await save(row);
|
|
49
49
|
*/
|
|
50
50
|
export declare function iterateCsvStream(chunks: AsyncIterable<string> | Iterable<string>, options?: object): AsyncGenerator<any, void, unknown>;
|
|
51
|
+
/**
|
|
52
|
+
* Serialize an async iterable of records as an async iterable of CSV
|
|
53
|
+
* text chunks — the pull form of `stringifyCsvChunks`, byte-identical
|
|
54
|
+
* to it for the same records: the header exactly once before the first
|
|
55
|
+
* object row, one line per chunk. Pull is the backpressure: the next
|
|
56
|
+
* record is requested only when the consumer asks for the next chunk,
|
|
57
|
+
* so a database cursor behind it never runs ahead of the socket in
|
|
58
|
+
* front of it. `options.signal` aborts between pulls (the rejection is
|
|
59
|
+
* the signal's reason); an abort, a consumer that stops early or a throw
|
|
60
|
+
* closes the record source exactly once.
|
|
61
|
+
* @param {AsyncIterable<Array|object>|Iterable<Array|object>} rows - Records
|
|
62
|
+
* @param {object} [options] - Writer options; see `stringifyCsv`, plus `signal`
|
|
63
|
+
* @yields {string} One line at a time
|
|
64
|
+
* @example
|
|
65
|
+
* response.body = stringifyCsvStream(store.collection('rows').query(doc), { signal });
|
|
66
|
+
*/
|
|
67
|
+
export declare function stringifyCsvStream(rows: AsyncIterable<any[] | object> | Iterable<any[] | object>, options?: object): AsyncGenerator<string, void, unknown>;
|
|
51
68
|
/**
|
|
52
69
|
* An incremental CSV writer: rows in, chunks out.
|
|
53
70
|
*/
|
|
@@ -56,12 +73,17 @@ export declare class CsvStreamWriter {
|
|
|
56
73
|
options: object;
|
|
57
74
|
chunks: any[];
|
|
58
75
|
onChunk: any;
|
|
59
|
-
|
|
60
|
-
|
|
76
|
+
formatter: {
|
|
77
|
+
lines: (row: any[] | object) => string[];
|
|
78
|
+
tail: () => string;
|
|
79
|
+
fields: () => string[] | null;
|
|
80
|
+
};
|
|
61
81
|
/**
|
|
62
82
|
* @param {object} [options] - Writer options; see `stringifyCsv`
|
|
63
83
|
*/
|
|
64
84
|
constructor(options?: object);
|
|
85
|
+
/** @returns {string[]|null} The columns as decided, once known. */
|
|
86
|
+
get fields(): string[] | null;
|
|
65
87
|
/**
|
|
66
88
|
* Write one record.
|
|
67
89
|
* @param {Array|object} row - An array, or an object keyed by column
|
|
@@ -80,7 +102,8 @@ export declare class CsvStreamWriter {
|
|
|
80
102
|
*/
|
|
81
103
|
toString(): string;
|
|
82
104
|
/**
|
|
83
|
-
* Finish writing
|
|
105
|
+
* Finish writing: the header an explicit field list is still owed
|
|
106
|
+
* goes out when no record was written.
|
|
84
107
|
* @returns {string} The complete document, or `''` with an `onChunk` sink
|
|
85
108
|
*/
|
|
86
109
|
end(): string;
|
|
@@ -100,3 +123,4 @@ export declare function createCsvStreamWriter(options?: {
|
|
|
100
123
|
onChunk?: (chunk: string) => void;
|
|
101
124
|
}): CsvStreamWriter;
|
|
102
125
|
export { stringifyCsvChunks, formatCsvValue };
|
|
126
|
+
export { JoslLimitError, CSV_LIMIT_CODES } from './limits.js';
|
package/dist/types/csv.d.ts
CHANGED
|
@@ -96,8 +96,10 @@ export declare function sniffCsvDialect(text: string, options?: object): {
|
|
|
96
96
|
};
|
|
97
97
|
/**
|
|
98
98
|
* Render one value as CSV cell text. `null`/`undefined` become empty,
|
|
99
|
-
* bigints lose the `n` suffix JOSL uses (CSV has no type marks),
|
|
100
|
-
* JOSL date classes and `Date` render as ISO-8601
|
|
99
|
+
* bigints lose the `n` suffix JOSL uses (CSV has no type marks), the
|
|
100
|
+
* JOSL date classes and `Date` render as ISO-8601, and a plain object or
|
|
101
|
+
* an array — a nested document in a flat format — is its JSON text, so
|
|
102
|
+
* a reader gets the value back instead of `[object Object]`.
|
|
101
103
|
* @param {*} value - The value
|
|
102
104
|
* @returns {string} Cell text, unquoted
|
|
103
105
|
*/
|
|
@@ -130,9 +132,28 @@ export declare function stringifyCsv(rows: Array<any[] | object>, options?: {
|
|
|
130
132
|
fields?: string[];
|
|
131
133
|
header?: boolean;
|
|
132
134
|
}): string;
|
|
135
|
+
/**
|
|
136
|
+
* The one row formatter behind every CSV writer: the same delimiter,
|
|
137
|
+
* quote and terminator rules and the same header-once rule, whether
|
|
138
|
+
* rows arrive as an array, an iterable, an async iterable or one at a
|
|
139
|
+
* time through the stream writer — so all of them produce byte-identical
|
|
140
|
+
* text for the same records by construction.
|
|
141
|
+
* @param {object} [options] - Writer options; see `stringifyCsv`
|
|
142
|
+
* @returns {{ lines: (row: Array|object) => string[], tail: () => string, fields: () => string[] | null }}
|
|
143
|
+
* `lines` formats one record as its line — preceded by the header
|
|
144
|
+
* line exactly once, before the first object row — `tail` answers the
|
|
145
|
+
* header an explicit field list is still owed when no record was
|
|
146
|
+
* ever written, `fields` the columns as decided
|
|
147
|
+
*/
|
|
148
|
+
export declare function createCsvRowFormatter(options?: object): {
|
|
149
|
+
lines: (row: any[] | object) => string[];
|
|
150
|
+
tail: () => string;
|
|
151
|
+
fields: () => string[] | null;
|
|
152
|
+
};
|
|
133
153
|
/**
|
|
134
154
|
* Serialize records as an iterable of chunks, one record at a time, so a
|
|
135
|
-
* large table never exists as a single string.
|
|
155
|
+
* large table never exists as a single string. The header precedes the
|
|
156
|
+
* first object row; a chunk is one line.
|
|
136
157
|
* @param {Iterable<Array|object>} rows - Records
|
|
137
158
|
* @param {object} [options] - Writer options; see `stringifyCsv`
|
|
138
159
|
* @yields {string} One record (or the header) at a time
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
export { parseJosl, parseToml } from './parse.js';
|
|
2
2
|
export { parseJoslCst, parseTomlCst, JoslCstDocument } from './cst.js';
|
|
3
|
-
export { createStreamReader, parseJoslStream } from './stream.js';
|
|
3
|
+
export { createStreamReader, parseJoslStream, iterateJoslStream } from './stream.js';
|
|
4
4
|
export { stringifyJosl, stringifyToml, formatKey, formatKeyPath, formatValue, formatSection, } from './stringify.js';
|
|
5
|
-
export { createStreamWriter, stringifyJoslChunks } from './write.js';
|
|
5
|
+
export { createStreamWriter, stringifyJoslChunks, stringifyJoslStream } from './write.js';
|
|
6
6
|
export { toGbnf, tomlToGbnf } from './gbnf.js';
|
|
7
7
|
export { parseJsonx, stringifyJsonx } from './jsonx.js';
|
|
8
8
|
export { createJsonxStreamReader, parseJsonxStream } from './jsonx-stream.js';
|
|
9
9
|
export { parseCsv, parseCsvDocument, stringifyCsv, stringifyCsvChunks, sniffCsvDialect, formatCsvValue, coerceCsvValue, CSV_CODES, } from './csv.js';
|
|
10
|
-
export { createCsvStreamReader, parseCsvStream, iterateCsvStream, createCsvStreamWriter, CsvStreamWriter, } from './csv-stream.js';
|
|
10
|
+
export { createCsvStreamReader, parseCsvStream, iterateCsvStream, stringifyCsvStream, createCsvStreamWriter, CsvStreamWriter, } from './csv-stream.js';
|
|
11
11
|
export { JoslSyntaxError, JoslStringifyError, JsonxSyntaxError, CsvSyntaxError } from './errors.js';
|
|
12
|
+
export { JoslLimitError, CSV_LIMIT_CODES, JOSL_LIMIT_CODES, JSONX_LIMIT_CODES } from './limits.js';
|
|
12
13
|
export { LocalDate, LocalTime, LocalDateTime, isValidDateParts, isValidTimeParts, } from './values.js';
|
|
@@ -3,6 +3,12 @@ export declare class JsonxMachine {
|
|
|
3
3
|
onEvent: ((event: JsonxStreamEvent) => void) | null;
|
|
4
4
|
partialText: boolean;
|
|
5
5
|
detach: (string | number)[] | null;
|
|
6
|
+
maxTotalBytes: number;
|
|
7
|
+
maxTokenBytes: number;
|
|
8
|
+
maxDepth: number;
|
|
9
|
+
maxRetainedValues: number;
|
|
10
|
+
totalBytes: number;
|
|
11
|
+
retained: number;
|
|
6
12
|
partialFrom: number;
|
|
7
13
|
partialHold: string;
|
|
8
14
|
buf: string;
|
|
@@ -42,6 +48,8 @@ export declare class JsonxMachine {
|
|
|
42
48
|
* @returns {this} The machine, for chaining
|
|
43
49
|
*/
|
|
44
50
|
feed(chunk: string): this;
|
|
51
|
+
token(buf: any, start: any, end: any): void;
|
|
52
|
+
retain(): void;
|
|
45
53
|
/**
|
|
46
54
|
* Finish the document, flushing any pending token.
|
|
47
55
|
* @returns {*} The completed root value
|
|
@@ -161,3 +169,4 @@ export declare function createJsonxStreamReader(options?: {
|
|
|
161
169
|
*/
|
|
162
170
|
export declare function parseJsonxStream(chunks: AsyncIterable<string> | Iterable<string>, options?: object): Promise<any>;
|
|
163
171
|
export { JsonxSyntaxError } from './errors.js';
|
|
172
|
+
export { JoslLimitError, JSONX_LIMIT_CODES } from './limits.js';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The CSV limits, by option name.
|
|
3
|
+
* @type {Readonly<Record<'CSV2001' | 'CSV2002' | 'CSV2003' | 'CSV2004', string>>}
|
|
4
|
+
*/
|
|
5
|
+
export declare const CSV_LIMIT_CODES: Readonly<Record<'CSV2001' | 'CSV2002' | 'CSV2003' | 'CSV2004', string>>;
|
|
6
|
+
/**
|
|
7
|
+
* The JOSL/TOML limits, by option name. A "record" is one logical line —
|
|
8
|
+
* the unit the reader buffers before it parses anything.
|
|
9
|
+
* @type {Readonly<Record<'JOSL2001' | 'JOSL2002' | 'JOSL2003' | 'JOSL2004' | 'JOSL2005', string>>}
|
|
10
|
+
*/
|
|
11
|
+
export declare const JOSL_LIMIT_CODES: Readonly<Record<'JOSL2001' | 'JOSL2002' | 'JOSL2003' | 'JOSL2004' | 'JOSL2005', string>>;
|
|
12
|
+
/**
|
|
13
|
+
* The JSONX/JSON limits, by option name.
|
|
14
|
+
* @type {Readonly<Record<'JSONX2001' | 'JSONX2002' | 'JSONX2003' | 'JSONX2004', string>>}
|
|
15
|
+
*/
|
|
16
|
+
export declare const JSONX_LIMIT_CODES: Readonly<Record<'JSONX2001' | 'JSONX2002' | 'JSONX2003' | 'JSONX2004', string>>;
|
|
17
|
+
/**
|
|
18
|
+
* Error thrown when a document crosses a limit a caller set. Never a
|
|
19
|
+
* repair, never healed: the text is refused where it stands.
|
|
20
|
+
*/
|
|
21
|
+
export declare class JoslLimitError extends Error {
|
|
22
|
+
code: string;
|
|
23
|
+
limit: number;
|
|
24
|
+
line: number | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* @param {string} code - The stable `CSV2xxx` / `JOSL2xxx` / `JSONX2xxx` code
|
|
27
|
+
* @param {string} message - What was crossed
|
|
28
|
+
* @param {number} limit - The limit the option set
|
|
29
|
+
* @param {number} [line] - 1-based physical line where the crossing was met, when known
|
|
30
|
+
*/
|
|
31
|
+
constructor(code: string, message: string, limit: number, line?: number);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Read one limit option: absent is `Infinity`; anything but a positive
|
|
35
|
+
* integer or `Infinity` is a `TypeError` — a limit of zero refuses every
|
|
36
|
+
* document, and a fraction or a string is a caller's mistake.
|
|
37
|
+
* @param {object} options - The reader/writer options
|
|
38
|
+
* @param {string} name - The option name
|
|
39
|
+
* @returns {number} The limit, `Infinity` when unset
|
|
40
|
+
*/
|
|
41
|
+
export declare function limitOption(options: object, name: string): number;
|
package/dist/types/machine.d.ts
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
export declare class JoslMachine {
|
|
2
2
|
mode: string;
|
|
3
3
|
onEvent: ((event: object) => void) | null;
|
|
4
|
+
detachRoot: any;
|
|
5
|
+
rootCount: number;
|
|
6
|
+
maxTotalBytes: number;
|
|
7
|
+
maxRecordBytes: number;
|
|
8
|
+
maxTokenBytes: number;
|
|
9
|
+
maxDepth: number;
|
|
10
|
+
maxRetainedValues: number;
|
|
11
|
+
totalBytes: number;
|
|
12
|
+
retained: number;
|
|
13
|
+
depth: number;
|
|
4
14
|
onLine: any;
|
|
5
15
|
lineValueStart: number;
|
|
6
16
|
lineValueEnd: number;
|
|
@@ -54,9 +64,15 @@ export declare class JoslMachine {
|
|
|
54
64
|
/**
|
|
55
65
|
* The (possibly still growing) root value: `{}`-rooted for documents,
|
|
56
66
|
* `[]`-rooted after a `[[]]` header. Undefined content yields `{}`.
|
|
67
|
+
* Under `detachRoot` a finished document hands its last item over
|
|
68
|
+
* here, so the root ends empty.
|
|
57
69
|
* @returns {*} Current root value
|
|
58
70
|
*/
|
|
59
71
|
root(): any;
|
|
72
|
+
detachLast(): void;
|
|
73
|
+
retain(): void;
|
|
74
|
+
deeper(depth: any): void;
|
|
75
|
+
token(line: any, start: any, end: any): void;
|
|
60
76
|
scan(): void;
|
|
61
77
|
cutLine(buf: any, start: any, nlPos: any, innerNl: any): void;
|
|
62
78
|
err(pos: any, message: any, hint: any): void;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Close an iterator once, swallowing what the close throws: a source
|
|
3
|
+
* that refuses its cancel is already gone.
|
|
4
|
+
* @param {Iterator<any> | AsyncIterator<any>} iterator
|
|
5
|
+
* @returns {Promise<void>}
|
|
6
|
+
*/
|
|
7
|
+
export declare function closeIterator(iterator: Iterator<any> | AsyncIterator<any>): Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* The rejection of an aborted pull: the signal's reason when it has one,
|
|
10
|
+
* else an `AbortError`-named error, as the platform spells it.
|
|
11
|
+
* @param {AbortSignal} signal
|
|
12
|
+
* @returns {unknown}
|
|
13
|
+
*/
|
|
14
|
+
export declare function abortedError(signal: AbortSignal): unknown;
|
package/dist/types/stream.d.ts
CHANGED
|
@@ -29,4 +29,24 @@ export declare function createStreamReader(options?: {
|
|
|
29
29
|
* @returns {Promise<*>} The completed root value
|
|
30
30
|
*/
|
|
31
31
|
export declare function parseJoslStream(chunks: AsyncIterable<string> | Iterable<string>, options?: object): Promise<any>;
|
|
32
|
+
/**
|
|
33
|
+
* Yield the `[[]]` root items of a JOSL document as they complete,
|
|
34
|
+
* without ever holding the whole document: each item is DETACHED from
|
|
35
|
+
* the root the moment the next `[[]]` header (or the end) completes it,
|
|
36
|
+
* so `root()`-style retention never grows past one record — the JOSL
|
|
37
|
+
* twin of the JSONX reader's `detach`. A document with a table root is
|
|
38
|
+
* refused by name (`JoslSyntaxError`): a table root is one retained
|
|
39
|
+
* value, not a stream of records. `options.signal` aborts between
|
|
40
|
+
* chunks; an abort, a consumer that stops early or a throw closes the
|
|
41
|
+
* chunk source exactly once.
|
|
42
|
+
* @param {AsyncIterable<string>|Iterable<string>} chunks - Source chunks
|
|
43
|
+
* @param {object} [options] - Reader options; see `createStreamReader`,
|
|
44
|
+
* plus `signal` and the `JOSL2xxx` limits
|
|
45
|
+
* @yields {object} One completed root item at a time
|
|
46
|
+
* @example
|
|
47
|
+
* for await (const record of iterateJoslStream(response.body))
|
|
48
|
+
* await save(record);
|
|
49
|
+
*/
|
|
50
|
+
export declare function iterateJoslStream(chunks: AsyncIterable<string> | Iterable<string>, options?: object): AsyncGenerator<object, void, unknown>;
|
|
32
51
|
export { JoslSyntaxError } from './errors.js';
|
|
52
|
+
export { JoslLimitError, JOSL_LIMIT_CODES } from './limits.js';
|
package/dist/types/util.d.ts
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
export declare const RE_DATETIME: RegExp;
|
|
3
3
|
/** A bare `HH:MM:SS` with an optional fraction. @type {RegExp} */
|
|
4
4
|
export declare const RE_TIMEONLY: RegExp;
|
|
5
|
+
/**
|
|
6
|
+
* Convert a local date and time with an offset to their native instant.
|
|
7
|
+
* ISO parsing preserves years 0000-0099 and truncates sub-millisecond
|
|
8
|
+
* precision consistently for JOSL and typed CSV. An invalid offset (or
|
|
9
|
+
* an instant Date cannot represent) returns an invalid Date for the
|
|
10
|
+
* caller's own refusal or text-preservation policy.
|
|
11
|
+
* @param {import('./values.js').LocalDate} date - Validated calendar date
|
|
12
|
+
* @param {import('./values.js').LocalTime} time - Validated local time
|
|
13
|
+
* @param {string} offset - Z/z or a signed HH:MM offset
|
|
14
|
+
* @returns {Date}
|
|
15
|
+
*/
|
|
16
|
+
export declare function offsetDateTime(date: import('./values.js').LocalDate, time: import('./values.js').LocalTime, offset: string): Date;
|
|
5
17
|
/**
|
|
6
18
|
* Run a sticky regex at `pos` and return its match (or null).
|
|
7
19
|
* @param {RegExp} re - A sticky (`y`) pattern
|
package/dist/types/write.d.ts
CHANGED
|
@@ -5,7 +5,9 @@ declare class JoslStreamWriter {
|
|
|
5
5
|
onRegExp: any;
|
|
6
6
|
};
|
|
7
7
|
onChunk: any;
|
|
8
|
+
buffered: boolean;
|
|
8
9
|
chunks: any[];
|
|
10
|
+
emitted: boolean;
|
|
9
11
|
ended: boolean;
|
|
10
12
|
rootIsArray: boolean;
|
|
11
13
|
rootHasPairs: boolean;
|
|
@@ -53,7 +55,8 @@ declare class JoslStreamWriter {
|
|
|
53
55
|
*/
|
|
54
56
|
comment(text: string): this;
|
|
55
57
|
/**
|
|
56
|
-
* The document text emitted so far
|
|
58
|
+
* The document text emitted so far; `''` under `buffer: false`, whose
|
|
59
|
+
* chunks went to the sink and nowhere else.
|
|
57
60
|
* @returns {string} Concatenated chunks
|
|
58
61
|
*/
|
|
59
62
|
text(): string;
|
|
@@ -66,12 +69,15 @@ declare class JoslStreamWriter {
|
|
|
66
69
|
/**
|
|
67
70
|
* Create a streaming JOSL/TOML writer - the write-side mirror of
|
|
68
71
|
* `createStreamReader`. Chunks are delivered through `onChunk` as they
|
|
69
|
-
* are produced and also accumulate for `text()` / `end()
|
|
72
|
+
* are produced and also accumulate for `text()` / `end()` — unless
|
|
73
|
+
* `buffer: false`, which keeps no copy: the sink is the only holder.
|
|
70
74
|
* @param {object} [options] - Writer options
|
|
71
75
|
* @param {'josl'|'toml'} [options.mode] - 'toml' emits strict TOML 1.0
|
|
72
76
|
* @param {'error'|'omit'} [options.onNull] - See `stringifyJosl`
|
|
73
77
|
* @param {'error'|'string'} [options.onRegExp] - See `stringifyJosl`
|
|
74
78
|
* @param {(chunk: string) => void} [options.onChunk] - Chunk sink
|
|
79
|
+
* @param {boolean} [options.buffer] - `false` retains no emitted text
|
|
80
|
+
* (needs `onChunk`); `text()` and `end()` then answer `''`
|
|
75
81
|
* @returns {JoslStreamWriter} The writer
|
|
76
82
|
*/
|
|
77
83
|
export declare function createStreamWriter(options?: {
|
|
@@ -79,6 +85,7 @@ export declare function createStreamWriter(options?: {
|
|
|
79
85
|
onNull?: 'error' | 'omit';
|
|
80
86
|
onRegExp?: 'error' | 'string';
|
|
81
87
|
onChunk?: (chunk: string) => void;
|
|
88
|
+
buffer?: boolean;
|
|
82
89
|
}): JoslStreamWriter;
|
|
83
90
|
/**
|
|
84
91
|
* Serialize a value as an iterable of text chunks: one chunk per record
|
|
@@ -89,4 +96,19 @@ export declare function createStreamWriter(options?: {
|
|
|
89
96
|
* @yields {string} Document chunks, in order
|
|
90
97
|
*/
|
|
91
98
|
export declare function stringifyJoslChunks(value: object | any[], options?: object): Generator<string, void, unknown>;
|
|
99
|
+
/**
|
|
100
|
+
* Serialize an async iterable of table records as an async iterable of
|
|
101
|
+
* `[[]]` chunks — the pull form of `stringifyJoslChunks` over a root
|
|
102
|
+
* array, byte-identical to it for the same records. Pull is the
|
|
103
|
+
* backpressure: the next record is requested only when the consumer
|
|
104
|
+
* asks for the next chunk. `options.signal` aborts between pulls (the
|
|
105
|
+
* rejection is the signal's reason); an abort, a consumer that stops
|
|
106
|
+
* early or a throw closes the record source exactly once.
|
|
107
|
+
* @param {AsyncIterable<object>|Iterable<object>} records - Table records
|
|
108
|
+
* @param {object} [options] - Writer options; see `stringifyJosl`, plus `signal`
|
|
109
|
+
* @yields {string} One `[[]]` record per chunk
|
|
110
|
+
* @example
|
|
111
|
+
* response.body = stringifyJoslStream(store.collection('rows').query(doc), { signal });
|
|
112
|
+
*/
|
|
113
|
+
export declare function stringifyJoslStream(records: AsyncIterable<object> | Iterable<object>, options?: object): AsyncGenerator<string, void, unknown>;
|
|
92
114
|
export { JoslStringifyError } from './errors.js';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jarenjs/josl",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.66.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"types": "./dist/types/index.d.ts",
|
|
@@ -99,6 +99,6 @@
|
|
|
99
99
|
"./package.json": "./package.json"
|
|
100
100
|
},
|
|
101
101
|
"dependencies": {
|
|
102
|
-
"@jarenjs/core": "^0.
|
|
102
|
+
"@jarenjs/core": "^0.66.1"
|
|
103
103
|
}
|
|
104
104
|
}
|