@helpers4/json 0.0.0-stage → 3.1.2-alpha.0

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/llms.txt ADDED
@@ -0,0 +1,699 @@
1
+ # @helpers4/json
2
+
3
+ > Tree-shakable TypeScript utility functions for the `json` domain.
4
+ > Package: `@helpers4/json` — Version: 3.1.2
5
+ > License: LGPL-3.0-or-later
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ npm install @helpers4/json
11
+ # or
12
+ pnpm add @helpers4/json
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```typescript
18
+ import { parse, parseLines, parseSequence, ... } from '@helpers4/json';
19
+ ```
20
+
21
+ ## Functions
22
+
23
+ | Function | Description |
24
+ |---|---|
25
+ | `parse` | Parses JSON permissively: strict JSON, JSONC (the dialect of `tsconfig.json` and VS Code's `settings |
26
+ | `parseLines` | Parses newline-delimited JSON — one JSON value per line. Covers both NDJSON (`application/x-ndjson`, |
27
+ | `parseSequence` | Parses a JSON text sequence (RFC 7464, `application/json-seq`): records that each start with the ASC |
28
+ | `parseStrict` | Parses strict JSON (RFC 8259) — exactly what `JSON.parse` accepts, nothing more. **Accepts:** stand |
29
+ | `safeParse` | Parses JSON permissively like parse — strict JSON, JSONC (HuJSON, JWCC) and JSON5 — but returns `nul |
30
+ | `safeParseLines` | Parses newline-delimited JSON (NDJSON, JSON Lines) like parseLines, but returns `null` (or `fallback |
31
+ | `safeParseSequence` | Parses a JSON text sequence (RFC 7464) like parseSequence, but returns `null` (or `fallback`) instea |
32
+ | `safeParseStrict` | Parses strict JSON like parseStrict, but returns `null` (or `fallback`) instead of throwing on inval |
33
+ | `stringify` | Serializes a value to JSON text — pretty-printed with 2 spaces by default, or canonical. **Formatti |
34
+ | `stringifyLines` | Serializes values as newline-delimited JSON — one compact JSON value per line, each followed by `\n` |
35
+ | `stringifySequence` | Serializes values as a JSON text sequence (RFC 7464, `application/json-seq`): each value is written |
36
+
37
+ ---
38
+
39
+ ## API Reference
40
+
41
+ ### `parse`
42
+
43
+ Parses JSON permissively: strict JSON, JSONC (the dialect of `tsconfig.json` and VS Code's
44
+ `settings.json`, also known as HuJSON or JWCC) and JSON5 — each a superset of the one before.
45
+
46
+ **Accepts:**
47
+ - everything parseStrict accepts (strict JSON, RFC 8259)
48
+ - comments: `// …` to the end of the line, and `/* … */`
49
+ - one trailing comma after the last item of an object or array
50
+ - object keys without quotes, when they are ECMAScript identifiers (`{ name: 1 }`, `{ $id: 1 }`)
51
+ - strings in single quotes, and in either quote: the escapes `\'`, `\v`, `\0` and `\xHH`, any
52
+ other escaped character standing for itself, and a backslash before a line break to continue
53
+ the string on the next line
54
+ - numbers with a leading `+`, a leading or trailing dot (`.5`, `5.`), in hexadecimal (`0x1F`),
55
+ and `Infinity`, `-Infinity` and `NaN`
56
+ - any Unicode whitespace between tokens, including a leading byte order mark (U+FEFF)
57
+
58
+ **Rejects:** more than one trailing comma in a row, leading zeros (`01`), octal or binary
59
+ numbers, unquoted keys that aren't identifiers (`{ my-key: 1 }`), raw line breaks inside a
60
+ string, an unterminated comment or string, `undefined`, and more than one value.
61
+
62
+ **On invalid input:** throws a `SyntaxError` naming the line and column of the first problem.
63
+
64
+ Strict JSON goes straight to the native `JSON.parse`, and JSONC to `JSON.parse` after its
65
+ comments are blanked out; only text that is neither goes through the JSON5 reader (written for
66
+ helpers4 from the JSON5 1.0.0 specification), so the common cases cost nothing extra.
67
+
68
+ Values are built as `JSON.parse` builds them: the last of duplicate keys wins, and a
69
+ `__proto__` key is an own property, never the prototype. `NaN` and `Infinity` have no JSON
70
+ representation — stringify writes them as `null`.
71
+
72
+ ```typescript
73
+ import { parse } from '@helpers4/json';
74
+
75
+ parse<T = unknown>(text: string): T
76
+ ```
77
+
78
+ **Parameters:**
79
+
80
+ - `text: string` — The JSON, JSONC or JSON5 text to parse
81
+
82
+ **Returns:** `T` — The parsed value, typed as `T`
83
+
84
+ **Examples:**
85
+
86
+ *Parse a config file with comments*
87
+
88
+ Reads JSONC — the tsconfig.json / settings.json dialect — as well as strict JSON.
89
+
90
+ ```typescript
91
+ parse(`{
92
+ // where the build goes
93
+ "outDir": "dist",
94
+ }`)
95
+ // => { outDir: 'dist' }
96
+ ```
97
+
98
+ *Invalid input throws*
99
+
100
+ The SyntaxError names the line and column of the problem.
101
+
102
+ ```typescript
103
+ parse('{ my-key: 1 }')
104
+ // throws SyntaxError: a key with a dash needs quotes
105
+ ```
106
+
107
+ *Parse JSON5*
108
+
109
+ Unquoted keys, single quotes, hexadecimal and leading-dot numbers, trailing commas.
110
+
111
+ ```typescript
112
+ parse("{ name: 'app', retries: 0x3, ratio: .5, }")
113
+ // => { name: 'app', retries: 3, ratio: 0.5 }
114
+ ```
115
+
116
+ ---
117
+
118
+ ### `parseLines`
119
+
120
+ Parses newline-delimited JSON — one JSON value per line. Covers both NDJSON
121
+ (`application/x-ndjson`, `.ndjson`) and JSON Lines (`.jsonl`), which are the same format.
122
+
123
+ **Accepts:**
124
+ - one strict JSON value per line (each line is read like parseStrict)
125
+ - `\n` and `\r\n` line endings
126
+ - a trailing line break after the last value, or none
127
+ - blank lines, skipped by default (see ParseLinesOptions.blankLines)
128
+ - an empty text, which holds zero values
129
+
130
+ **Rejects:** comments, trailing commas and any other non-strict JSON on a line, a value spread
131
+ over several lines, and a leading byte order mark (JSON Lines forbids it).
132
+
133
+ **On invalid input:** throws a `SyntaxError` naming the 1-based line number, with the
134
+ original parse error as its `cause` — unless `invalidLines`/`blankLines` say to skip it.
135
+
136
+ ```typescript
137
+ import { parseLines } from '@helpers4/json';
138
+
139
+ parseLines<T = unknown>(text: string, options: ParseLinesOptions): T[]
140
+ ```
141
+
142
+ **Parameters:**
143
+
144
+ - `text: string` — The newline-delimited JSON text
145
+ - `options: ParseLinesOptions` (default: `{}`) — How to handle blank and invalid lines — see ParseLinesOptions
146
+
147
+ **Returns:** `T[]` — The parsed values, in line order, typed as `T[]`
148
+
149
+ **Examples:**
150
+
151
+ *Read NDJSON / JSON Lines*
152
+
153
+ One JSON value per line; a trailing line break is optional.
154
+
155
+ ```typescript
156
+ parseLines('{"id":1}\n{"id":2}\n')
157
+ // => [{ id: 1 }, { id: 2 }]
158
+ ```
159
+
160
+ *Skip a truncated last line*
161
+
162
+ With invalidLines: "skip", a line cut by a crash is dropped instead of failing the whole parse.
163
+
164
+ ```typescript
165
+ parseLines('{"id":1}\n{"id":', { invalidLines: 'skip' })
166
+ // => [{ id: 1 }]
167
+ ```
168
+
169
+ *Fail on an invalid line by default*
170
+
171
+ The SyntaxError names the line.
172
+
173
+ ```typescript
174
+ parseLines('1\n{\n3')
175
+ // throws SyntaxError: parseLines: line 2: ...
176
+ ```
177
+
178
+ ---
179
+
180
+ ### `parseSequence`
181
+
182
+ Parses a JSON text sequence (RFC 7464, `application/json-seq`): records that each start with
183
+ the ASCII record separator (RS, U+001E) and hold one JSON text, as written by `jq --seq`.
184
+
185
+ **Accepts:**
186
+ - one strict JSON value per record (each record is read like parseStrict)
187
+ - JSON values spread over several lines inside a record — unlike parseLines
188
+ - several RS in a row (empty records are ignored, as the RFC's grammar allows)
189
+ - an empty text, which holds zero records
190
+
191
+ **Rejects (as invalid records):**
192
+ - anything before the first RS
193
+ - a record that isn't strict JSON, or holds only whitespace
194
+ - a top-level number, `true`, `false` or `null` not followed by whitespace — RFC 7464 §2.4
195
+ requires dropping it, since it may have been truncated
196
+
197
+ **On invalid input:** throws a `SyntaxError` naming the 1-based record number, with the
198
+ original parse error (if any) as its `cause` — unless `invalidRecords: 'skip'` says to drop it.
199
+
200
+ ```typescript
201
+ import { parseSequence } from '@helpers4/json';
202
+
203
+ parseSequence<T = unknown>(text: string, options: ParseSequenceOptions): T[]
204
+ ```
205
+
206
+ **Parameters:**
207
+
208
+ - `text: string` — The JSON text sequence
209
+ - `options: ParseSequenceOptions` (default: `{}`) — How to handle invalid records — see ParseSequenceOptions
210
+
211
+ **Returns:** `T[]` — The parsed values, in record order, typed as `T[]`
212
+
213
+ **Examples:**
214
+
215
+ *Read a JSON text sequence*
216
+
217
+ Each record starts with the record separator U+001E, as jq --seq writes it.
218
+
219
+ ```typescript
220
+ parseSequence('\u001E{"id":1}\n\u001E{"id":2}\n')
221
+ // => [{ id: 1 }, { id: 2 }]
222
+ ```
223
+
224
+ *Recover from a truncated log*
225
+
226
+ RFC 7464 recommends skipping invalid records; ask for it with invalidRecords: "skip".
227
+
228
+ ```typescript
229
+ parseSequence('\u001E{"id":1}\n\u001E{"id', { invalidRecords: 'skip' })
230
+ // => [{ id: 1 }]
231
+ ```
232
+
233
+ ---
234
+
235
+ ### `parseStrict`
236
+
237
+ Parses strict JSON (RFC 8259) — exactly what `JSON.parse` accepts, nothing more.
238
+
239
+ **Accepts:** standard JSON only — double-quoted strings and keys, finite decimal numbers,
240
+ `true`/`false`/`null`, objects and arrays, surrounding whitespace.
241
+
242
+ **Rejects:** comments, trailing commas, single quotes, unquoted keys, hexadecimal numbers,
243
+ `NaN`/`Infinity`, a leading byte order mark (U+FEFF), and anything else that isn't strict JSON.
244
+
245
+ **On invalid input:** throws the native `SyntaxError` from `JSON.parse`.
246
+
247
+ Use it to validate input that must be strict JSON, e.g. an API payload. To also read config
248
+ files with comments or JSON5, use parse. To never throw, use safeParseStrict.
249
+
250
+ ```typescript
251
+ import { parseStrict } from '@helpers4/json';
252
+
253
+ parseStrict<T = unknown>(text: string): T
254
+ ```
255
+
256
+ **Parameters:**
257
+
258
+ - `text: string` — The JSON text to parse
259
+
260
+ **Returns:** `T` — The parsed value, typed as `T`
261
+
262
+ **Examples:**
263
+
264
+ *Parse strict JSON*
265
+
266
+ Exactly what JSON.parse accepts, typed with a generic.
267
+
268
+ ```typescript
269
+ parseStrict<{ id: number }>('{"id":1}')
270
+ // => { id: 1 }
271
+ ```
272
+
273
+ *Reject anything that is not strict JSON*
274
+
275
+ Comments and trailing commas throw a SyntaxError: use it to validate an API payload.
276
+
277
+ ```typescript
278
+ parseStrict('{"id":1,}')
279
+ // throws SyntaxError
280
+ ```
281
+
282
+ ---
283
+
284
+ ### `safeParse`
285
+
286
+ Parses JSON permissively like parse — strict JSON, JSONC (HuJSON, JWCC) and JSON5 —
287
+ but returns `null` (or `fallback`) instead of throwing on invalid input.
288
+
289
+ **Accepts / rejects:** exactly what parse does — see there for the full list.
290
+
291
+ **On invalid input:** returns `fallback` when one is given — even `undefined`, which is told
292
+ apart from an omitted argument — and `null` otherwise. Never throws.
293
+
294
+ ```typescript
295
+ import { safeParse } from '@helpers4/json';
296
+
297
+ safeParse<T = unknown>(text: string): T | null
298
+ ```
299
+
300
+ **Parameters:**
301
+
302
+ - `text: string` — The JSON, JSONC or JSON5 text to parse
303
+
304
+ **Returns:** `T | null` — The parsed value typed as `T`, or `fallback` on invalid input
305
+
306
+ ```typescript
307
+ import { safeParse } from '@helpers4/json';
308
+
309
+ safeParse<T = unknown, F = T>(text: string, fallback: F): T | F
310
+ ```
311
+
312
+ **Parameters:**
313
+
314
+ - `text: string` — The JSON, JSONC or JSON5 text to parse
315
+ - `fallback: F` — Value returned on invalid input. Defaults to `null` when omitted.
316
+
317
+ **Returns:** `T | F` — The parsed value typed as `T`, or `fallback` on invalid input
318
+
319
+ **Examples:**
320
+
321
+ *Read JSONC or JSON5 without throwing*
322
+
323
+ Same parsing as parse, but returns null on invalid input.
324
+
325
+ ```typescript
326
+ safeParse('{"a": 1, /* note */}') // => { a: 1 }
327
+ safeParse('{a: }') // => null
328
+ ```
329
+
330
+ *Fall back to defaults*
331
+
332
+ Pass a fallback to get a usable value even when the file is broken.
333
+
334
+ ```typescript
335
+ safeParse('{ broken', {})
336
+ // => {}
337
+ ```
338
+
339
+ ---
340
+
341
+ ### `safeParseLines`
342
+
343
+ Parses newline-delimited JSON (NDJSON, JSON Lines) like parseLines, but returns `null`
344
+ (or `fallback`) instead of throwing on invalid input.
345
+
346
+ **Accepts / rejects:** exactly what parseLines does, with the same
347
+ ParseLinesOptions.
348
+
349
+ **On invalid input:** returns `fallback` when one is given — even `undefined`, which is told
350
+ apart from an omitted argument — and `null` otherwise. Never throws. To pass `options` without
351
+ a fallback, pass `null` as the fallback.
352
+
353
+ ```typescript
354
+ import { safeParseLines } from '@helpers4/json';
355
+
356
+ safeParseLines<T = unknown>(text: string): T[] | null
357
+ ```
358
+
359
+ **Parameters:**
360
+
361
+ - `text: string` — The newline-delimited JSON text
362
+
363
+ **Returns:** `T[] | null` — The parsed values typed as `T[]`, or `fallback` on invalid input
364
+
365
+ ```typescript
366
+ import { safeParseLines } from '@helpers4/json';
367
+
368
+ safeParseLines<T = unknown, F = T[]>(text: string, fallback: F, options?: ParseLinesOptions): F | T[]
369
+ ```
370
+
371
+ **Parameters:**
372
+
373
+ - `text: string` — The newline-delimited JSON text
374
+ - `fallback: F` — Value returned on invalid input. Defaults to `null` when omitted.
375
+ - `options?: ParseLinesOptions` — How to handle blank and invalid lines — see ParseLinesOptions
376
+
377
+ **Returns:** `F | T[]` — The parsed values typed as `T[]`, or `fallback` on invalid input
378
+
379
+ **Examples:**
380
+
381
+ *Read NDJSON without throwing*
382
+
383
+ Returns null if any line is invalid.
384
+
385
+ ```typescript
386
+ safeParseLines('1\n2\n') // => [1, 2]
387
+ safeParseLines('1\n{') // => null
388
+ ```
389
+
390
+ *Options without a fallback*
391
+
392
+ Pass null as the fallback to give options.
393
+
394
+ ```typescript
395
+ safeParseLines('1\n\n2', null, { blankLines: 'fail' })
396
+ // => null (JSON Lines forbids blank lines)
397
+ ```
398
+
399
+ ---
400
+
401
+ ### `safeParseSequence`
402
+
403
+ Parses a JSON text sequence (RFC 7464) like parseSequence, but returns `null` (or
404
+ `fallback`) instead of throwing on invalid input.
405
+
406
+ **Accepts / rejects:** exactly what parseSequence does, with the same
407
+ ParseSequenceOptions.
408
+
409
+ **On invalid input:** returns `fallback` when one is given — even `undefined`, which is told
410
+ apart from an omitted argument — and `null` otherwise. Never throws. To pass `options` without
411
+ a fallback, pass `null` as the fallback.
412
+
413
+ ```typescript
414
+ import { safeParseSequence } from '@helpers4/json';
415
+
416
+ safeParseSequence<T = unknown>(text: string): T[] | null
417
+ ```
418
+
419
+ **Parameters:**
420
+
421
+ - `text: string` — The JSON text sequence
422
+
423
+ **Returns:** `T[] | null` — The parsed values typed as `T[]`, or `fallback` on invalid input
424
+
425
+ ```typescript
426
+ import { safeParseSequence } from '@helpers4/json';
427
+
428
+ safeParseSequence<T = unknown, F = T[]>(text: string, fallback: F, options?: ParseSequenceOptions): F | T[]
429
+ ```
430
+
431
+ **Parameters:**
432
+
433
+ - `text: string` — The JSON text sequence
434
+ - `fallback: F` — Value returned on invalid input. Defaults to `null` when omitted.
435
+ - `options?: ParseSequenceOptions` — How to handle invalid records — see ParseSequenceOptions
436
+
437
+ **Returns:** `F | T[]` — The parsed values typed as `T[]`, or `fallback` on invalid input
438
+
439
+ **Examples:**
440
+
441
+ *Read a sequence without throwing*
442
+
443
+ Returns null if any record is invalid.
444
+
445
+ ```typescript
446
+ safeParseSequence('\u001E1\n') // => [1]
447
+ safeParseSequence('\u001E{\n') // => null
448
+ ```
449
+
450
+ *Provide a fallback*
451
+
452
+ The fallback is returned on invalid input.
453
+
454
+ ```typescript
455
+ safeParseSequence('\u001E{\n', [])
456
+ // => []
457
+ ```
458
+
459
+ ---
460
+
461
+ ### `safeParseStrict`
462
+
463
+ Parses strict JSON like parseStrict, but returns `null` (or `fallback`) instead of
464
+ throwing on invalid input.
465
+
466
+ **Accepts / rejects:** exactly what parseStrict does — standard JSON only, no comments,
467
+ no trailing commas, no JSON5 syntax, no leading byte order mark.
468
+
469
+ **On invalid input:** returns `fallback` when one is given — even `undefined`, which is told
470
+ apart from an omitted argument — and `null` otherwise. Never throws.
471
+
472
+ ```typescript
473
+ import { safeParseStrict } from '@helpers4/json';
474
+
475
+ safeParseStrict<T = unknown>(text: string): T | null
476
+ ```
477
+
478
+ **Parameters:**
479
+
480
+ - `text: string` — The JSON text to parse
481
+
482
+ **Returns:** `T | null` — The parsed value typed as `T`, or `fallback` on invalid input
483
+
484
+ ```typescript
485
+ import { safeParseStrict } from '@helpers4/json';
486
+
487
+ safeParseStrict<T = unknown, F = T>(text: string, fallback: F): T | F
488
+ ```
489
+
490
+ **Parameters:**
491
+
492
+ - `text: string` — The JSON text to parse
493
+ - `fallback: F` — Value returned on invalid input. Defaults to `null` when omitted.
494
+
495
+ **Returns:** `T | F` — The parsed value typed as `T`, or `fallback` on invalid input
496
+
497
+ **Examples:**
498
+
499
+ *Parse without throwing*
500
+
501
+ Returns null instead of throwing on invalid input.
502
+
503
+ ```typescript
504
+ safeParseStrict('{"id":1}') // => { id: 1 }
505
+ safeParseStrict('{"id":1,}') // => null
506
+ ```
507
+
508
+ *Provide a fallback*
509
+
510
+ The fallback is returned on invalid input, so the result is never null.
511
+
512
+ ```typescript
513
+ safeParseStrict('oops', { id: 0 })
514
+ // => { id: 0 }
515
+ ```
516
+
517
+ ---
518
+
519
+ ### `stringify`
520
+
521
+ Serializes a value to JSON text — pretty-printed with 2 spaces by default, or canonical.
522
+
523
+ **Formatting:**
524
+ - default — indented with 2 spaces, like a hand-written config file
525
+ - `{ indent: n }` — indented with `n` spaces (an integer from 0 to 10); `{ indent: 0 }` gives a
526
+ compact single line, the same as `JSON.stringify(value)`
527
+ - `'canonical'` — canonical JSON (RFC 8785, JSON Canonicalization Scheme): object keys sorted
528
+ by UTF-16 code units, no whitespace at all, numbers in their shortest ECMAScript form. Two
529
+ equal values always give the same bytes, so the output can be hashed, signed or compared.
530
+ Canonical output can't be indented — the RFC fixes every byte.
531
+
532
+ **Same as `JSON.stringify`:** `toJSON()` is called (a `Date` becomes its ISO string),
533
+ `undefined`, functions and symbols are dropped from objects and become `null` in arrays, and
534
+ `Number`/`String`/`Boolean` objects are unwrapped. Outside `'canonical'`, `NaN` and `±Infinity`
535
+ silently become `null`, as there.
536
+
537
+ **Throws** instead of returning `undefined` or dropping data silently:
538
+ - a `TypeError` when `value` itself is `undefined`, a function or a symbol
539
+ - a `TypeError` on a circular structure or a `BigInt` (like `JSON.stringify`)
540
+ - with `'canonical'`, a `RangeError` on `NaN`, `±Infinity` or a string with a lone surrogate,
541
+ which canonical JSON can't hold
542
+ - a `RangeError` when `indent` isn't an integer from 0 to 10
543
+
544
+ The output is strict JSON, so it is also valid JSONC and JSON5.
545
+
546
+ ```typescript
547
+ import { stringify } from '@helpers4/json';
548
+
549
+ stringify(value: unknown, formatting: StringifyFormatting): string
550
+ ```
551
+
552
+ **Parameters:**
553
+
554
+ - `value: unknown` — The value to serialize
555
+ - `formatting: StringifyFormatting` (default: `{}`) — `{ indent }` (default `{ indent: 2 }`) or `'canonical'` — see StringifyFormatting
556
+
557
+ **Returns:** `string` — The JSON text
558
+
559
+ **Examples:**
560
+
561
+ *Pretty-print by default*
562
+
563
+ Indented with 2 spaces, like a hand-written config file.
564
+
565
+ ```typescript
566
+ stringify({ name: 'app', tags: ['a'] })
567
+ // => '{\n "name": "app",\n "tags": [\n "a"\n ]\n}'
568
+ ```
569
+
570
+ *Compact output*
571
+
572
+ indent: 0 gives a single line, like JSON.stringify(value).
573
+
574
+ ```typescript
575
+ stringify({ b: 1, a: 2 }, { indent: 0 })
576
+ // => '{"b":1,"a":2}'
577
+ ```
578
+
579
+ *Canonical JSON (RFC 8785)*
580
+
581
+ Sorted keys and no whitespace: equal values always give the same bytes, ready to hash or sign.
582
+
583
+ ```typescript
584
+ stringify({ b: 1, a: 2 }, 'canonical')
585
+ // => '{"a":2,"b":1}'
586
+ ```
587
+
588
+ ---
589
+
590
+ ### `stringifyLines`
591
+
592
+ Serializes values as newline-delimited JSON — one compact JSON value per line, each followed
593
+ by `\n`. The output is both NDJSON and JSON Lines (`.jsonl`), which are the same format.
594
+
595
+ **Formatting:**
596
+ - default — each value compact, like `JSON.stringify(value)`
597
+ - `'canonical'` — each value as canonical JSON (RFC 8785): sorted keys, reproducible bytes, for
598
+ hashing or diffing logs line by line
599
+
600
+ Indentation isn't available: a line break inside a value would split it across lines. String
601
+ values can't break a line either — `JSON.stringify` escapes `\n` and `\r` inside them.
602
+
603
+ **Output:** every value is followed by `\n`, including the last one (NDJSON requires it, and
604
+ JSON Lines recommends it, so files can be concatenated). An empty array gives `''`.
605
+
606
+ **Throws** a `TypeError` when `values` isn't an array, or when one of them has no JSON
607
+ representation (`undefined`, a function, a symbol, a `BigInt`, a circular structure) — rather
608
+ than writing a line that would read back as something else. With `'canonical'`, also a
609
+ `RangeError` on `NaN`, `±Infinity` or a lone surrogate (see stringify).
610
+
611
+ ```typescript
612
+ import { stringifyLines } from '@helpers4/json';
613
+
614
+ stringifyLines(values: readonly unknown[], formatting?: "canonical"): string
615
+ ```
616
+
617
+ **Parameters:**
618
+
619
+ - `values: readonly unknown[]` — The values to write, one per line
620
+ - `formatting?: "canonical"` — `'canonical'` for canonical JSON lines; compact JSON by default
621
+
622
+ **Returns:** `string` — The newline-delimited JSON text
623
+
624
+ **Examples:**
625
+
626
+ *Write NDJSON / JSON Lines*
627
+
628
+ One compact value per line, each followed by a line break.
629
+
630
+ ```typescript
631
+ stringifyLines([{ id: 1 }, { id: 2 }])
632
+ // => '{"id":1}\n{"id":2}\n'
633
+ ```
634
+
635
+ *Canonical lines*
636
+
637
+ Each line in canonical form, so a log can be hashed or diffed line by line.
638
+
639
+ ```typescript
640
+ stringifyLines([{ b: 1, a: 2 }], 'canonical')
641
+ // => '{"a":2,"b":1}\n'
642
+ ```
643
+
644
+ ---
645
+
646
+ ### `stringifySequence`
647
+
648
+ Serializes values as a JSON text sequence (RFC 7464, `application/json-seq`): each value is
649
+ written as the ASCII record separator (RS, U+001E), the compact JSON text, then `\n` — the
650
+ exact framing the RFC requires of encoders, as `jq --seq` writes it.
651
+
652
+ **Formatting:**
653
+ - default — each value compact, like `JSON.stringify(value)`
654
+ - `'canonical'` — each value as canonical JSON (RFC 8785): sorted keys, reproducible bytes
655
+
656
+ Indentation isn't offered, for symmetry with stringifyLines: the trailing `\n` is what
657
+ lets a reader detect a truncated top-level number, `true`, `false` or `null` (RFC 7464 §2.4).
658
+
659
+ **Output:** an empty array gives `''`.
660
+
661
+ **Throws** a `TypeError` when `values` isn't an array, or when one of them has no JSON
662
+ representation (`undefined`, a function, a symbol, a `BigInt`, a circular structure). With
663
+ `'canonical'`, also a `RangeError` on `NaN`, `±Infinity` or a lone surrogate (see
664
+ stringify).
665
+
666
+ ```typescript
667
+ import { stringifySequence } from '@helpers4/json';
668
+
669
+ stringifySequence(values: readonly unknown[], formatting?: "canonical"): string
670
+ ```
671
+
672
+ **Parameters:**
673
+
674
+ - `values: readonly unknown[]` — The values to write, one per record
675
+ - `formatting?: "canonical"` — `'canonical'` for canonical JSON records; compact JSON by default
676
+
677
+ **Returns:** `string` — The JSON text sequence
678
+
679
+ **Examples:**
680
+
681
+ *Write a JSON text sequence*
682
+
683
+ Each value is framed as RS, JSON text, line feed (RFC 7464).
684
+
685
+ ```typescript
686
+ stringifySequence([{ id: 1 }, 2])
687
+ // => '\u001E{"id":1}\n\u001E2\n'
688
+ ```
689
+
690
+ *Canonical records*
691
+
692
+ Each record in canonical form.
693
+
694
+ ```typescript
695
+ stringifySequence([{ b: 1, a: 2 }], 'canonical')
696
+ // => '\u001E{"a":2,"b":1}\n'
697
+ ```
698
+
699
+ ---