@jarenjs/josl 0.34.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/FORMAT.md +235 -0
- package/README.md +494 -0
- package/dist/types/cst.d.ts +78 -0
- package/dist/types/csv-machine.d.ts +104 -0
- package/dist/types/csv-stream.d.ts +102 -0
- package/dist/types/csv.d.ts +141 -0
- package/dist/types/errors.d.ts +79 -0
- package/dist/types/gbnf.d.ts +20 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/jsonx-scalar.d.ts +92 -0
- package/dist/types/jsonx-stream.d.ts +163 -0
- package/dist/types/jsonx.d.ts +31 -0
- package/dist/types/machine.d.ts +97 -0
- package/dist/types/parse.d.ts +24 -0
- package/dist/types/stream.d.ts +32 -0
- package/dist/types/stringify.d.ts +63 -0
- package/dist/types/util.d.ts +56 -0
- package/dist/types/values.d.ts +60 -0
- package/dist/types/write.d.ts +92 -0
- package/package.json +104 -0
- package/schemas/jaren-josl-data.schema.json +21 -0
- package/src/cst.js +256 -0
- package/src/csv-machine.js +908 -0
- package/src/csv-stream.js +196 -0
- package/src/csv.js +363 -0
- package/src/errors.js +103 -0
- package/src/gbnf.js +179 -0
- package/src/index.js +50 -0
- package/src/jsonx-scalar.js +326 -0
- package/src/jsonx-stream.js +806 -0
- package/src/jsonx.js +342 -0
- package/src/machine.js +1252 -0
- package/src/parse.js +37 -0
- package/src/stream.js +57 -0
- package/src/stringify.js +341 -0
- package/src/util.js +96 -0
- package/src/values.js +104 -0
- package/src/write.js +226 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { stringifyCsvChunks, formatCsvValue } from './csv.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create an incremental CSV reader.
|
|
4
|
+
*
|
|
5
|
+
* Events, in document order:
|
|
6
|
+
* {type:'header', fields, line} - column names resolved
|
|
7
|
+
* {type:'row', index, values, record, line} - a record completed
|
|
8
|
+
* {type:'repair', code, message, line, column} - damage healed
|
|
9
|
+
*
|
|
10
|
+
* @param {object} [options] - Reader options; see `parseCsv`
|
|
11
|
+
* @returns {{feed(chunk: string): void, end(): Array, rows(): Array,
|
|
12
|
+
* fields(): string[]|null, repairs(): object[]}} The reader: `feed`
|
|
13
|
+
* accepts chunks that may split any field, `end` flushes the pending
|
|
14
|
+
* record and returns every row, and the rest peek at partial results.
|
|
15
|
+
* @example
|
|
16
|
+
* const reader = createCsvStreamReader({ headers: true, onEvent: console.log });
|
|
17
|
+
* reader.feed('a,b\n1,');
|
|
18
|
+
* reader.feed('2\n');
|
|
19
|
+
* reader.end(); // [{ a: '1', b: '2' }]
|
|
20
|
+
*/
|
|
21
|
+
export declare function createCsvStreamReader(options?: object): {
|
|
22
|
+
feed(chunk: string): void;
|
|
23
|
+
end(): any[];
|
|
24
|
+
rows(): any[];
|
|
25
|
+
fields(): string[] | null;
|
|
26
|
+
repairs(): object[];
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Read an async iterable of string chunks (a fetch body, a file stream,
|
|
30
|
+
* an LLM response) into records.
|
|
31
|
+
* @param {AsyncIterable<string>|Iterable<string>} chunks - Source chunks
|
|
32
|
+
* @param {object} [options] - Reader options; see `parseCsv`
|
|
33
|
+
* @returns {Promise<Array>} Every record
|
|
34
|
+
*/
|
|
35
|
+
export declare function parseCsvStream(chunks: AsyncIterable<string> | Iterable<string>, options?: object): Promise<any[]>;
|
|
36
|
+
/**
|
|
37
|
+
* Yield records as they complete, without ever holding the whole table.
|
|
38
|
+
*
|
|
39
|
+
* This is the reason to stream: `parseCsvStream` still accumulates every
|
|
40
|
+
* row, so a file larger than memory needs a reader that hands each record
|
|
41
|
+
* over and forgets it. Rows are dropped from the machine as they are
|
|
42
|
+
* yielded, so memory stays flat in the number of records.
|
|
43
|
+
* @param {AsyncIterable<string>|Iterable<string>} chunks - Source chunks
|
|
44
|
+
* @param {object} [options] - Reader options; see `parseCsv`
|
|
45
|
+
* @yields {Array|object} One record at a time
|
|
46
|
+
* @example
|
|
47
|
+
* for await (const row of iterateCsvStream(response.body, { headers: true }))
|
|
48
|
+
* await save(row);
|
|
49
|
+
*/
|
|
50
|
+
export declare function iterateCsvStream(chunks: AsyncIterable<string> | Iterable<string>, options?: object): AsyncGenerator<any, void, unknown>;
|
|
51
|
+
/**
|
|
52
|
+
* An incremental CSV writer: rows in, chunks out.
|
|
53
|
+
*/
|
|
54
|
+
export declare class CsvStreamWriter {
|
|
55
|
+
#private;
|
|
56
|
+
options: object;
|
|
57
|
+
chunks: any[];
|
|
58
|
+
onChunk: any;
|
|
59
|
+
fields: any;
|
|
60
|
+
wroteHeader: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* @param {object} [options] - Writer options; see `stringifyCsv`
|
|
63
|
+
*/
|
|
64
|
+
constructor(options?: object);
|
|
65
|
+
/**
|
|
66
|
+
* Write one record.
|
|
67
|
+
* @param {Array|object} row - An array, or an object keyed by column
|
|
68
|
+
* @returns {this} The writer, for chaining
|
|
69
|
+
*/
|
|
70
|
+
write(row: any[] | object): this;
|
|
71
|
+
/**
|
|
72
|
+
* Write many records.
|
|
73
|
+
* @param {Iterable<Array|object>} rows - Records
|
|
74
|
+
* @returns {this} The writer, for chaining
|
|
75
|
+
*/
|
|
76
|
+
writeAll(rows: Iterable<any[] | object>): this;
|
|
77
|
+
/**
|
|
78
|
+
* Concatenate the buffered chunks; empty when an `onChunk` sink is set.
|
|
79
|
+
* @returns {string} The document so far
|
|
80
|
+
*/
|
|
81
|
+
toString(): string;
|
|
82
|
+
/**
|
|
83
|
+
* Finish writing.
|
|
84
|
+
* @returns {string} The complete document, or `''` with an `onChunk` sink
|
|
85
|
+
*/
|
|
86
|
+
end(): string;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Create an incremental CSV writer.
|
|
90
|
+
* @param {object} [options] - Writer options; see `stringifyCsv`
|
|
91
|
+
* @param {(chunk: string) => void} [options.onChunk] - Chunk sink; without
|
|
92
|
+
* one, chunks buffer until `end()`
|
|
93
|
+
* @returns {CsvStreamWriter} The writer
|
|
94
|
+
* @example
|
|
95
|
+
* const w = createCsvStreamWriter();
|
|
96
|
+
* w.write({ a: 1, b: 2 }).write({ a: 3, b: 4 });
|
|
97
|
+
* w.end(); // 'a,b\r\n1,2\r\n3,4\r\n'
|
|
98
|
+
*/
|
|
99
|
+
export declare function createCsvStreamWriter(options?: {
|
|
100
|
+
onChunk?: (chunk: string) => void;
|
|
101
|
+
}): CsvStreamWriter;
|
|
102
|
+
export { stringifyCsvChunks, formatCsvValue };
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { CSV_CODES, coerceCsvValue } from './csv-machine.js';
|
|
2
|
+
import { CsvSyntaxError } from './errors.js';
|
|
3
|
+
/**
|
|
4
|
+
* Parse a complete CSV document.
|
|
5
|
+
*
|
|
6
|
+
* Default is **strict**: anything RFC 4180 does not allow throws a
|
|
7
|
+
* `CsvSyntaxError` carrying a `CSV1xxx` code, a line and a column.
|
|
8
|
+
* `repair: true` instead takes the reading that loses the least data,
|
|
9
|
+
* records it, and carries on — see `parseCsvDocument` for the log.
|
|
10
|
+
*
|
|
11
|
+
* @param {string} text - CSV source text
|
|
12
|
+
* @param {object} [options] - Reader options
|
|
13
|
+
* @param {string} [options.delimiter=','] - Field separator, one character
|
|
14
|
+
* @param {string|null} [options.quote='"'] - Quote character; `null` disables quoting
|
|
15
|
+
* @param {string|null} [options.comment] - Lines starting with it are skipped
|
|
16
|
+
* @param {boolean|string[]} [options.headers=false] - `true` reads the first
|
|
17
|
+
* record as column names, an array supplies them; either way records become
|
|
18
|
+
* objects. `false` yields arrays, which allocate less.
|
|
19
|
+
* @param {boolean} [options.repair=false] - Heal damage instead of throwing
|
|
20
|
+
* @param {boolean} [options.trim=false] - Trim spaces/tabs around unquoted fields
|
|
21
|
+
* @param {boolean} [options.typed=false] - Coerce numbers, bigints, booleans,
|
|
22
|
+
* `null` and ISO dates; quoted cells always stay strings
|
|
23
|
+
* @param {boolean} [options.emptyAsNull=false] - Empty cells become `null`
|
|
24
|
+
* @param {boolean} [options.skipEmptyLines] - Drop blank lines (default: on
|
|
25
|
+
* in repair mode, off otherwise)
|
|
26
|
+
* @param {(event: object) => void} [options.onEvent] - Document-order event sink
|
|
27
|
+
* @param {(repair: object) => void} [options.onRepair] - Repair sink
|
|
28
|
+
* @returns {Array<string[]|object>} The records
|
|
29
|
+
* @throws {CsvSyntaxError} On malformed input, unless `repair` is set
|
|
30
|
+
* @example
|
|
31
|
+
* parseCsv('a,b\n1,2'); // [['a','b'], ['1','2']]
|
|
32
|
+
* parseCsv('a,b\n1,2', { headers: true }); // [{ a: '1', b: '2' }]
|
|
33
|
+
*/
|
|
34
|
+
export declare function parseCsv(text: string, options?: {
|
|
35
|
+
delimiter?: string;
|
|
36
|
+
quote?: string | null;
|
|
37
|
+
comment?: string | null;
|
|
38
|
+
headers?: boolean | string[];
|
|
39
|
+
repair?: boolean;
|
|
40
|
+
trim?: boolean;
|
|
41
|
+
typed?: boolean;
|
|
42
|
+
emptyAsNull?: boolean;
|
|
43
|
+
skipEmptyLines?: boolean;
|
|
44
|
+
onEvent?: (event: object) => void;
|
|
45
|
+
onRepair?: (repair: object) => void;
|
|
46
|
+
}): Array<string[] | object>;
|
|
47
|
+
/**
|
|
48
|
+
* Parse a complete CSV document and report everything that was learned
|
|
49
|
+
* about it: the resolved dialect, the column names, the records, and the
|
|
50
|
+
* repair log. This is the form to use when the input is untrusted — it is
|
|
51
|
+
* the only way to see *what* had to be healed.
|
|
52
|
+
* @param {string} text - CSV source text
|
|
53
|
+
* @param {object} [options] - Reader options; see `parseCsv`. `delimiter:
|
|
54
|
+
* 'auto'` sniffs the dialect from the text first.
|
|
55
|
+
* @returns {{dialect: object, fields: string[]|null, rows: Array, repairs: object[]}}
|
|
56
|
+
* The document
|
|
57
|
+
* @example
|
|
58
|
+
* const doc = parseCsvDocument('a;b\n1;2', { delimiter: 'auto', headers: true });
|
|
59
|
+
* doc.dialect.delimiter; // ';'
|
|
60
|
+
* doc.rows; // [{ a: '1', b: '2' }]
|
|
61
|
+
*/
|
|
62
|
+
export declare function parseCsvDocument(text: string, options?: object): {
|
|
63
|
+
dialect: object;
|
|
64
|
+
fields: string[] | null;
|
|
65
|
+
rows: any[];
|
|
66
|
+
repairs: object[];
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Guess a CSV document's dialect from its text.
|
|
70
|
+
*
|
|
71
|
+
* The delimiter is chosen by consistency: each candidate is parsed and
|
|
72
|
+
* scored by how often it produces the same field count, because a
|
|
73
|
+
* separator that is really a separator divides every record the same way
|
|
74
|
+
* and one that is not divides them arbitrarily. Ties go to the delimiter
|
|
75
|
+
* yielding more columns, since a spurious separator can only ever split
|
|
76
|
+
* fewer.
|
|
77
|
+
*
|
|
78
|
+
* Header detection asks whether the first record looks unlike the rest:
|
|
79
|
+
* if every cell in it is a non-numeric string while some column below is
|
|
80
|
+
* consistently numeric or date-like, that first record is naming columns
|
|
81
|
+
* rather than carrying data.
|
|
82
|
+
*
|
|
83
|
+
* @param {string} text - CSV source text (a prefix is enough)
|
|
84
|
+
* @param {object} [options] - Reader options that affect parsing (`quote`, `comment`)
|
|
85
|
+
* @returns {{delimiter: string, quote: string|null, headers: boolean, width: number, confidence: number}}
|
|
86
|
+
* The guess, with `confidence` in 0..1
|
|
87
|
+
* @example
|
|
88
|
+
* sniffCsvDialect('a;b;c\n1;2;3'); // { delimiter: ';', headers: true, ... }
|
|
89
|
+
*/
|
|
90
|
+
export declare function sniffCsvDialect(text: string, options?: object): {
|
|
91
|
+
delimiter: string;
|
|
92
|
+
quote: string | null;
|
|
93
|
+
headers: boolean;
|
|
94
|
+
width: number;
|
|
95
|
+
confidence: number;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
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), and the
|
|
100
|
+
* JOSL date classes and `Date` render as ISO-8601.
|
|
101
|
+
* @param {*} value - The value
|
|
102
|
+
* @returns {string} Cell text, unquoted
|
|
103
|
+
*/
|
|
104
|
+
export declare function formatCsvValue(value: any): string;
|
|
105
|
+
/**
|
|
106
|
+
* Serialize records to a CSV document.
|
|
107
|
+
*
|
|
108
|
+
* A field is quoted only when it has to be — when it contains the
|
|
109
|
+
* delimiter, a quote, a newline, or edge whitespace a lenient reader
|
|
110
|
+
* might trim. Everything else is written bare, which keeps the output
|
|
111
|
+
* both smaller and diff-friendly.
|
|
112
|
+
*
|
|
113
|
+
* @param {Array<Array|object>} rows - Records: arrays, or objects keyed by column
|
|
114
|
+
* @param {object} [options] - Writer options
|
|
115
|
+
* @param {string} [options.delimiter=','] - Field separator
|
|
116
|
+
* @param {string} [options.quote='"'] - Quote character
|
|
117
|
+
* @param {string} [options.newline='\r\n'] - Record terminator; RFC 4180 §2.1
|
|
118
|
+
* specifies CRLF, which is also what spreadsheet software expects
|
|
119
|
+
* @param {string[]} [options.fields] - Column order; inferred from the first
|
|
120
|
+
* object record when omitted
|
|
121
|
+
* @param {boolean} [options.header=true] - Emit a header row for object records
|
|
122
|
+
* @returns {string} The CSV document
|
|
123
|
+
* @example
|
|
124
|
+
* stringifyCsv([{ a: 1, b: 'x,y' }]); // 'a,b\r\n1,"x,y"\r\n'
|
|
125
|
+
*/
|
|
126
|
+
export declare function stringifyCsv(rows: Array<any[] | object>, options?: {
|
|
127
|
+
delimiter?: string;
|
|
128
|
+
quote?: string;
|
|
129
|
+
newline?: string;
|
|
130
|
+
fields?: string[];
|
|
131
|
+
header?: boolean;
|
|
132
|
+
}): string;
|
|
133
|
+
/**
|
|
134
|
+
* Serialize records as an iterable of chunks, one record at a time, so a
|
|
135
|
+
* large table never exists as a single string.
|
|
136
|
+
* @param {Iterable<Array|object>} rows - Records
|
|
137
|
+
* @param {object} [options] - Writer options; see `stringifyCsv`
|
|
138
|
+
* @yields {string} One record (or the header) at a time
|
|
139
|
+
*/
|
|
140
|
+
export declare function stringifyCsvChunks(rows: Iterable<any[] | object>, options?: object): Generator<string, void, unknown>;
|
|
141
|
+
export { CsvSyntaxError, CSV_CODES, coerceCsvValue };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared constructor body for the line/column syntax errors. The `name`
|
|
3
|
+
* is passed as a string literal because the bundle is minified and a
|
|
4
|
+
* mangled class name must not leak into `error.name`.
|
|
5
|
+
*/
|
|
6
|
+
declare class LineColumnSyntaxError extends SyntaxError {
|
|
7
|
+
line: number;
|
|
8
|
+
column: number;
|
|
9
|
+
hint: string | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* @param {string} name - The public class name for `error.name`
|
|
12
|
+
* @param {string} message - What is wrong
|
|
13
|
+
* @param {number} line - 1-based physical line number
|
|
14
|
+
* @param {number} column - 1-based column number
|
|
15
|
+
* @param {string} [hint] - Repair suggestion for machine-repair loops
|
|
16
|
+
*/
|
|
17
|
+
constructor(name: string, message: string, line: number, column: number, hint?: string);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Error thrown when JOSL / TOML source text violates the grammar or the
|
|
21
|
+
* table redefinition rules.
|
|
22
|
+
*/
|
|
23
|
+
export declare class JoslSyntaxError extends LineColumnSyntaxError {
|
|
24
|
+
/**
|
|
25
|
+
* @param {string} message - What is wrong
|
|
26
|
+
* @param {number} line - 1-based physical line number
|
|
27
|
+
* @param {number} column - 1-based column number
|
|
28
|
+
* @param {string} [hint] - Repair suggestion for machine-repair loops
|
|
29
|
+
*/
|
|
30
|
+
constructor(message: string, line: number, column: number, hint?: string);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Error thrown when a value cannot be represented in the requested output
|
|
34
|
+
* mode (e.g. `null` or a RegExp in strict TOML mode, a scalar root).
|
|
35
|
+
*/
|
|
36
|
+
export declare class JoslStringifyError extends Error {
|
|
37
|
+
path: (string | number)[];
|
|
38
|
+
/**
|
|
39
|
+
* @param {string} message - What is wrong
|
|
40
|
+
* @param {(string|number)[]} [path] - Path of the offending value
|
|
41
|
+
*/
|
|
42
|
+
constructor(message: string, path?: (string | number)[]);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Error thrown when CSV source text violates RFC 4180 in strict mode.
|
|
46
|
+
*
|
|
47
|
+
* Every condition this reports is also a *repairable* one: `repair: true`
|
|
48
|
+
* turns each into a logged repair instead of a throw, so the same `code`
|
|
49
|
+
* appears either as `error.code` here or as an entry in the reader's
|
|
50
|
+
* repair log. Carrying the code both ways is what lets a caller move
|
|
51
|
+
* between the two modes without re-learning the diagnosis.
|
|
52
|
+
*/
|
|
53
|
+
export declare class CsvSyntaxError extends SyntaxError {
|
|
54
|
+
code: string;
|
|
55
|
+
line: number;
|
|
56
|
+
column: number;
|
|
57
|
+
hint: string | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* @param {string} code - Stable `CSV1xxx` diagnosis code
|
|
60
|
+
* @param {string} message - What is wrong
|
|
61
|
+
* @param {number} line - 1-based physical line number
|
|
62
|
+
* @param {number} column - 1-based column number
|
|
63
|
+
* @param {string} [hint] - Repair suggestion for machine-repair loops
|
|
64
|
+
*/
|
|
65
|
+
constructor(code: string, message: string, line: number, column: number, hint?: string);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Error thrown when JSONX source text violates the grammar.
|
|
69
|
+
*/
|
|
70
|
+
export declare class JsonxSyntaxError extends LineColumnSyntaxError {
|
|
71
|
+
/**
|
|
72
|
+
* @param {string} message - What is wrong
|
|
73
|
+
* @param {number} line - 1-based physical line number
|
|
74
|
+
* @param {number} column - 1-based column number
|
|
75
|
+
* @param {string} [hint] - Repair suggestion for machine-repair loops
|
|
76
|
+
*/
|
|
77
|
+
constructor(message: string, line: number, column: number, hint?: string);
|
|
78
|
+
}
|
|
79
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a GBNF grammar for raw JOSL or TOML text.
|
|
3
|
+
*
|
|
4
|
+
* The result is a complete grammar string with `root` as its entry rule,
|
|
5
|
+
* ready to hand to a llama.cpp-family sampler. It constrains syntax only —
|
|
6
|
+
* a document it accepts still has to go through `parseJosl` for the rules
|
|
7
|
+
* a context-free grammar cannot express (duplicate keys, table conflicts).
|
|
8
|
+
* @param {object} [options] - Grammar options
|
|
9
|
+
* @param {'josl'|'toml'} [options.mode] - 'toml' omits the JOSL-only value
|
|
10
|
+
* forms (null, bigint, regexp) and the `[[]]` root-array header
|
|
11
|
+
* @returns {string} The GBNF grammar
|
|
12
|
+
*/
|
|
13
|
+
export declare function toGbnf(options?: {
|
|
14
|
+
mode?: 'josl' | 'toml';
|
|
15
|
+
}): string;
|
|
16
|
+
/**
|
|
17
|
+
* Build a GBNF grammar for strict TOML 1.0 text.
|
|
18
|
+
* @returns {string} The GBNF grammar
|
|
19
|
+
*/
|
|
20
|
+
export declare function tomlToGbnf(): string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { parseJosl, parseToml } from './parse.js';
|
|
2
|
+
export { parseJoslCst, parseTomlCst, JoslCstDocument } from './cst.js';
|
|
3
|
+
export { createStreamReader, parseJoslStream } from './stream.js';
|
|
4
|
+
export { stringifyJosl, stringifyToml, formatKey, formatKeyPath, formatValue, formatSection, } from './stringify.js';
|
|
5
|
+
export { createStreamWriter, stringifyJoslChunks } from './write.js';
|
|
6
|
+
export { toGbnf, tomlToGbnf } from './gbnf.js';
|
|
7
|
+
export { parseJsonx, stringifyJsonx } from './jsonx.js';
|
|
8
|
+
export { createJsonxStreamReader, parseJsonxStream } from './jsonx-stream.js';
|
|
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';
|
|
11
|
+
export { JoslSyntaxError, JoslStringifyError, JsonxSyntaxError, CsvSyntaxError } from './errors.js';
|
|
12
|
+
export { LocalDate, LocalTime, LocalDateTime, isValidDateParts, isValidTimeParts, } from './values.js';
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export type JsonxErrCallback = (pos: number, message: string, hint?: string) => never;
|
|
2
|
+
export type JsonxCheckEndCallback = (pos: number) => void;
|
|
3
|
+
/**
|
|
4
|
+
* @callback JsonxErrCallback
|
|
5
|
+
* @param {number} pos - Offset of the error in the source text
|
|
6
|
+
* @param {string} message - What is wrong
|
|
7
|
+
* @param {string} [hint] - Repair suggestion
|
|
8
|
+
* @returns {never} Must throw
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* @callback JsonxCheckEndCallback
|
|
12
|
+
* @param {number} pos - Offset of the first character after the value
|
|
13
|
+
* @returns {void} Must throw when the character cannot follow a value
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Whether a char code may legally follow a completed JSON/JSONX value
|
|
17
|
+
* (whitespace, `,`, `]` or `}`).
|
|
18
|
+
* @param {number} c - The char code
|
|
19
|
+
* @returns {boolean}
|
|
20
|
+
*/
|
|
21
|
+
export declare function isValueEndCode(c: number): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Decode one escape sequence.
|
|
24
|
+
* @param {string} text - Source text
|
|
25
|
+
* @param {number} pos - Offset of the backslash
|
|
26
|
+
* @param {JsonxErrCallback} err - Error reporter
|
|
27
|
+
* @returns {[string, number]} The decoded text and the offset after it
|
|
28
|
+
*/
|
|
29
|
+
export declare function decodeEscape(text: string, pos: number, err: JsonxErrCallback): [string, number];
|
|
30
|
+
/**
|
|
31
|
+
* Decode a double-quoted string.
|
|
32
|
+
* @param {string} text - Source text
|
|
33
|
+
* @param {number} pos - Offset of the opening quote
|
|
34
|
+
* @param {JsonxErrCallback} err - Error reporter
|
|
35
|
+
* @returns {[string, number]} The value and the offset after the close quote
|
|
36
|
+
*/
|
|
37
|
+
export declare function decodeString(text: string, pos: number, err: JsonxErrCallback): [string, number];
|
|
38
|
+
/**
|
|
39
|
+
* Decode the body of a string between two offsets, stopping at the closing
|
|
40
|
+
* quote (which it does not consume) or at `stop`. In `partial` mode it also
|
|
41
|
+
* stops before an escape that is not complete within the span, so a
|
|
42
|
+
* still-arriving string can be decoded as far as it is safe to.
|
|
43
|
+
* @param {string} text - Source text
|
|
44
|
+
* @param {number} from - Offset of the first body character
|
|
45
|
+
* @param {number} stop - Exclusive end offset
|
|
46
|
+
* @param {JsonxErrCallback} err - Error reporter
|
|
47
|
+
* @param {boolean} partial - Whether the span may end mid-escape
|
|
48
|
+
* @returns {[string, number]} The decoded text and the offset reached
|
|
49
|
+
*/
|
|
50
|
+
export declare function decodeStringSpan(text: string, from: number, stop: number, err: JsonxErrCallback, partial: boolean): [string, number];
|
|
51
|
+
/**
|
|
52
|
+
* Try to match an RFC 3339 datetime token (date, time, local or offset
|
|
53
|
+
* date-time; `T`, `t` or a single space may separate date and time).
|
|
54
|
+
* @param {string} text - Source text
|
|
55
|
+
* @param {number} pos - Offset of the first digit
|
|
56
|
+
* @param {JsonxErrCallback} err - Error reporter
|
|
57
|
+
* @param {JsonxCheckEndCallback} checkEnd - Value-terminator check
|
|
58
|
+
* @returns {[*, number]|null} The value and end offset, or null if the
|
|
59
|
+
* text at `pos` is not a datetime
|
|
60
|
+
*/
|
|
61
|
+
export declare function matchDateTime(text: string, pos: number, err: JsonxErrCallback, checkEnd: JsonxCheckEndCallback): [any, number] | null;
|
|
62
|
+
/**
|
|
63
|
+
* Match a number token. Strict JSON follows RFC 8259; JSONX adds a
|
|
64
|
+
* leading `+`, `_` separators, an `n` bigint suffix, and auto-promotes
|
|
65
|
+
* unsafe integers to BigInt.
|
|
66
|
+
* @param {string} text - Source text
|
|
67
|
+
* @param {number} pos - Offset of the first sign or digit
|
|
68
|
+
* @param {'jsonx'|'json'} mode - Dialect
|
|
69
|
+
* @param {JsonxErrCallback} err - Error reporter
|
|
70
|
+
* @param {JsonxCheckEndCallback} checkEnd - Value-terminator check
|
|
71
|
+
* @returns {[number|bigint, number]} The value and end offset
|
|
72
|
+
*/
|
|
73
|
+
export declare function matchNumber(text: string, pos: number, mode: 'jsonx' | 'json', err: JsonxErrCallback, checkEnd: JsonxCheckEndCallback): [number | bigint, number];
|
|
74
|
+
/**
|
|
75
|
+
* Match a word token (`true`, `false`, `null`, and in JSONX the
|
|
76
|
+
* optionally signed non-finite spellings `inf`/`Infinity`/`nan`/`NaN`).
|
|
77
|
+
* The caller performs the value-terminator check.
|
|
78
|
+
* @param {string} text - Source text
|
|
79
|
+
* @param {number} pos - Offset of the first sign or letter
|
|
80
|
+
* @param {'jsonx'|'json'} mode - Dialect
|
|
81
|
+
* @param {JsonxErrCallback} err - Error reporter
|
|
82
|
+
* @returns {[boolean|null|number, number]} The value and end offset
|
|
83
|
+
*/
|
|
84
|
+
export declare function matchWord(text: string, pos: number, mode: 'jsonx' | 'json', err: JsonxErrCallback): [boolean | null | number, number];
|
|
85
|
+
/**
|
|
86
|
+
* Match a regexp literal. The caller performs the value-terminator check.
|
|
87
|
+
* @param {string} text - Source text
|
|
88
|
+
* @param {number} pos - Offset of the opening slash
|
|
89
|
+
* @param {JsonxErrCallback} err - Error reporter
|
|
90
|
+
* @returns {[RegExp, number]} The value and end offset
|
|
91
|
+
*/
|
|
92
|
+
export declare function matchRegExp(text: string, pos: number, err: JsonxErrCallback): [RegExp, number];
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
export declare class JsonxMachine {
|
|
2
|
+
mode: string;
|
|
3
|
+
onEvent: ((event: JsonxStreamEvent) => void) | null;
|
|
4
|
+
partialText: boolean;
|
|
5
|
+
detach: (string | number)[] | null;
|
|
6
|
+
partialFrom: number;
|
|
7
|
+
partialHold: string;
|
|
8
|
+
buf: string;
|
|
9
|
+
pos: number;
|
|
10
|
+
state: number;
|
|
11
|
+
stack: any[];
|
|
12
|
+
path: any[];
|
|
13
|
+
rootValue: any;
|
|
14
|
+
ended: boolean;
|
|
15
|
+
curLine: number;
|
|
16
|
+
lineStart: number;
|
|
17
|
+
scanPos: number;
|
|
18
|
+
scanInFlags: boolean;
|
|
19
|
+
scanInClass: boolean;
|
|
20
|
+
scanDtSpace: boolean;
|
|
21
|
+
errCb: (pos: any, message: any, hint: any) => void;
|
|
22
|
+
endCb: (pos: any) => void;
|
|
23
|
+
/**
|
|
24
|
+
* @param {object} [options] - Reader options
|
|
25
|
+
* @param {'jsonx'|'json'} [options.mode] - 'json' rejects every JSONX
|
|
26
|
+
* extension (bigint, regexp, datetime, non-finite, separators, +)
|
|
27
|
+
* @param {(event: JsonxStreamEvent) => void} [options.onEvent] - Event sink
|
|
28
|
+
* @param {boolean} [options.partialText] - Also emit `text-partial`
|
|
29
|
+
* deltas while a string value is still arriving
|
|
30
|
+
* @param {(string|number)[]} [options.detach] - Path pattern whose
|
|
31
|
+
* matching values are NOT retained in the root (see `detachPattern`)
|
|
32
|
+
*/
|
|
33
|
+
constructor(options?: {
|
|
34
|
+
mode?: 'jsonx' | 'json';
|
|
35
|
+
onEvent?: (event: JsonxStreamEvent) => void;
|
|
36
|
+
partialText?: boolean;
|
|
37
|
+
detach?: (string | number)[];
|
|
38
|
+
});
|
|
39
|
+
/**
|
|
40
|
+
* Feed the next chunk of source text; chunks may split any token.
|
|
41
|
+
* @param {string} chunk - Next piece of the document
|
|
42
|
+
* @returns {this} The machine, for chaining
|
|
43
|
+
*/
|
|
44
|
+
feed(chunk: string): this;
|
|
45
|
+
/**
|
|
46
|
+
* Finish the document, flushing any pending token.
|
|
47
|
+
* @returns {*} The completed root value
|
|
48
|
+
* @throws {JsonxSyntaxError} When the document is incomplete or invalid
|
|
49
|
+
*/
|
|
50
|
+
end(): any;
|
|
51
|
+
/**
|
|
52
|
+
* The (possibly still growing) root value. Undefined until the root
|
|
53
|
+
* value has started; container members appear as they complete.
|
|
54
|
+
*
|
|
55
|
+
* Under `detach`, matching values were never linked in — the root is
|
|
56
|
+
* the document's *frame* (its header members, and an empty array where
|
|
57
|
+
* the detached records would have been), which is the whole point.
|
|
58
|
+
* @returns {*} Current root value
|
|
59
|
+
*/
|
|
60
|
+
root(): any;
|
|
61
|
+
errAt(pos: any, message: any, hint: any): void;
|
|
62
|
+
endError(): void;
|
|
63
|
+
pump(): void;
|
|
64
|
+
compact(): void;
|
|
65
|
+
parseValueAt(buf: any, pos: any, c: any): any;
|
|
66
|
+
decodeScalarToken(buf: any, pos: any, c: any): number;
|
|
67
|
+
checkValueEnd(buf: any, pos: any): void;
|
|
68
|
+
emitPartialText(buf: any, pos: any, stop: any, open: any): void;
|
|
69
|
+
valuePath(): any[];
|
|
70
|
+
/**
|
|
71
|
+
* Whether `this.path` plus one more segment matches the detach
|
|
72
|
+
* pattern. Spelled out rather than built on `valuePath()` so the
|
|
73
|
+
* common case — a document read with no `detach` at all — costs one
|
|
74
|
+
* null check, and the matching case costs no array allocation.
|
|
75
|
+
*/
|
|
76
|
+
detaches(last: any): boolean;
|
|
77
|
+
completeScalar(value: any): void;
|
|
78
|
+
openContainer(isArray: any): void;
|
|
79
|
+
closeContainer(): void;
|
|
80
|
+
scanString(buf: any, pos: any): any;
|
|
81
|
+
scanRegExp(buf: any, pos: any): any;
|
|
82
|
+
scanScalar(buf: any, pos: any): any;
|
|
83
|
+
}
|
|
84
|
+
export type JsonxStreamPath = (string | number)[];
|
|
85
|
+
export type JsonxStreamEvent = ({
|
|
86
|
+
type: 'object-start' | 'array-start';
|
|
87
|
+
path: JsonxStreamPath;
|
|
88
|
+
line: number;
|
|
89
|
+
} | {
|
|
90
|
+
type: 'object-end' | 'array-end';
|
|
91
|
+
path: JsonxStreamPath;
|
|
92
|
+
value: any;
|
|
93
|
+
line: number;
|
|
94
|
+
} | {
|
|
95
|
+
type: 'pair';
|
|
96
|
+
path: JsonxStreamPath;
|
|
97
|
+
key: string;
|
|
98
|
+
value: any;
|
|
99
|
+
line: number;
|
|
100
|
+
} | {
|
|
101
|
+
type: 'item';
|
|
102
|
+
path: JsonxStreamPath;
|
|
103
|
+
index: number;
|
|
104
|
+
value: any;
|
|
105
|
+
line: number;
|
|
106
|
+
} | {
|
|
107
|
+
type: 'text-partial';
|
|
108
|
+
path: JsonxStreamPath;
|
|
109
|
+
text: string;
|
|
110
|
+
line: number;
|
|
111
|
+
});
|
|
112
|
+
/**
|
|
113
|
+
* Absolute document path: strings for object keys, numbers for array
|
|
114
|
+
* indices (JSON-Pointer-able).
|
|
115
|
+
* @typedef {(string|number)[]} JsonxStreamPath
|
|
116
|
+
*/
|
|
117
|
+
/**
|
|
118
|
+
* Document-order reader event; see the module doc comment for semantics.
|
|
119
|
+
* @typedef {(
|
|
120
|
+
* {type: 'object-start'|'array-start', path: JsonxStreamPath, line: number}
|
|
121
|
+
* | {type: 'object-end'|'array-end', path: JsonxStreamPath, value: *, line: number}
|
|
122
|
+
* | {type: 'pair', path: JsonxStreamPath, key: string, value: *, line: number}
|
|
123
|
+
* | {type: 'item', path: JsonxStreamPath, index: number, value: *, line: number}
|
|
124
|
+
* | {type: 'text-partial', path: JsonxStreamPath, text: string, line: number}
|
|
125
|
+
* )} JsonxStreamEvent
|
|
126
|
+
*/
|
|
127
|
+
/**
|
|
128
|
+
* Create an incremental JSONX / strict-JSON reader.
|
|
129
|
+
* @param {object} [options] - Reader options
|
|
130
|
+
* @param {'jsonx'|'json'} [options.mode] - 'json' rejects every JSONX
|
|
131
|
+
* extension and matches `JSON.parse` for accepted documents
|
|
132
|
+
* @param {(event: JsonxStreamEvent) => void} [options.onEvent] - Event sink
|
|
133
|
+
* @param {boolean} [options.partialText] - Also emit `text-partial` deltas
|
|
134
|
+
* while a string value is still arriving, for progressive display
|
|
135
|
+
* @param {(string|number)[]} [options.detach] - Path pattern (segments,
|
|
136
|
+
* or `'*'` for any one segment) whose matching values are never linked
|
|
137
|
+
* into the tree. Their completion events still carry them, so the
|
|
138
|
+
* consumer sees every record and the reader retains none — this is what
|
|
139
|
+
* makes a document larger than memory readable.
|
|
140
|
+
* @returns {{feed(chunk: string): void, end(): *, root(): *}} The reader:
|
|
141
|
+
* `feed` accepts chunks that may split any token, `end` flushes,
|
|
142
|
+
* validates completeness and returns the root, `root` peeks at the
|
|
143
|
+
* partial result.
|
|
144
|
+
* @throws {TypeError} On a malformed `detach` pattern
|
|
145
|
+
*/
|
|
146
|
+
export declare function createJsonxStreamReader(options?: {
|
|
147
|
+
mode?: 'jsonx' | 'json';
|
|
148
|
+
onEvent?: (event: JsonxStreamEvent) => void;
|
|
149
|
+
partialText?: boolean;
|
|
150
|
+
detach?: (string | number)[];
|
|
151
|
+
}): {
|
|
152
|
+
feed(chunk: string): void;
|
|
153
|
+
end(): any;
|
|
154
|
+
root(): any;
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* Parse an async iterable of string chunks (e.g. an LLM output stream).
|
|
158
|
+
* @param {AsyncIterable<string>|Iterable<string>} chunks - Source chunks
|
|
159
|
+
* @param {object} [options] - Reader options; see `createJsonxStreamReader`
|
|
160
|
+
* @returns {Promise<*>} The completed root value
|
|
161
|
+
*/
|
|
162
|
+
export declare function parseJsonxStream(chunks: AsyncIterable<string> | Iterable<string>, options?: object): Promise<any>;
|
|
163
|
+
export { JsonxSyntaxError } from './errors.js';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse JSONX (or, with `mode: 'json'`, strict JSON) text.
|
|
3
|
+
* @param {string} text - Source text
|
|
4
|
+
* @param {object} [options] - Parser options
|
|
5
|
+
* @param {'jsonx'|'json'} [options.mode] - 'json' matches JSON.parse
|
|
6
|
+
* @param {(event: object) => void} [options.onEvent] - Document-order
|
|
7
|
+
* event sink: {type:'open', path, kind}, {type:'value', path, value},
|
|
8
|
+
* {type:'close', path, value} — paths are absolute (JSON-Pointer-able)
|
|
9
|
+
* @returns {*} The parsed value
|
|
10
|
+
* @throws {JsonxSyntaxError} On invalid input
|
|
11
|
+
*/
|
|
12
|
+
export declare function parseJsonx(text: string, options?: {
|
|
13
|
+
mode?: 'jsonx' | 'json';
|
|
14
|
+
onEvent?: (event: object) => void;
|
|
15
|
+
}): any;
|
|
16
|
+
/**
|
|
17
|
+
* Serialize a value to JSONX text. With `mode: 'json'` this delegates to
|
|
18
|
+
* `JSON.stringify` for exact backward compatibility (bigints throw, dates
|
|
19
|
+
* become quoted strings, non-finite numbers become null, ...).
|
|
20
|
+
* @param {*} value - The value to serialize
|
|
21
|
+
* @param {object} [options] - Writer options
|
|
22
|
+
* @param {'jsonx'|'json'} [options.mode] - Output dialect
|
|
23
|
+
* @param {number|string} [options.indent] - Pretty-print indentation
|
|
24
|
+
* @returns {string|undefined} The text, or undefined for undefined input
|
|
25
|
+
*/
|
|
26
|
+
export declare function stringifyJsonx(value: any, options?: {
|
|
27
|
+
mode?: 'jsonx' | 'json';
|
|
28
|
+
indent?: number | string;
|
|
29
|
+
}): string | undefined;
|
|
30
|
+
export { JsonxSyntaxError } from './errors.js';
|
|
31
|
+
export { LocalDate, LocalTime, LocalDateTime } from './values.js';
|