unclosed 1.0.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/CHANGELOG.md +19 -0
- package/LICENSE +21 -0
- package/README.md +136 -0
- package/dist/cjs/index.js +20 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/parse.js +377 -0
- package/dist/cjs/parse.js.map +1 -0
- package/dist/cjs/stream.js +98 -0
- package/dist/cjs/stream.js.map +1 -0
- package/dist/esm/index.js +8 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/parse.js +367 -0
- package/dist/esm/parse.js.map +1 -0
- package/dist/esm/stream.js +93 -0
- package/dist/esm/stream.js.map +1 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/parse.d.ts +77 -0
- package/dist/types/stream.d.ts +54 -0
- package/package.json +58 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Incremental helpers for feeding model output in as it arrives.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PartialJson = void 0;
|
|
7
|
+
exports.parseStream = parseStream;
|
|
8
|
+
const parse_js_1 = require("./parse.js");
|
|
9
|
+
/**
|
|
10
|
+
* Accumulates chunks and exposes the best-effort value after each one.
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* const acc = new PartialJson()
|
|
14
|
+
* for await (const chunk of tokens) {
|
|
15
|
+
* const snapshot = acc.push(chunk)
|
|
16
|
+
* render(snapshot.value)
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
class PartialJson {
|
|
21
|
+
constructor(options = {}) {
|
|
22
|
+
this.buffer = "";
|
|
23
|
+
this.options = options;
|
|
24
|
+
this.snapshot = {
|
|
25
|
+
value: undefined,
|
|
26
|
+
complete: false,
|
|
27
|
+
incompletePath: null,
|
|
28
|
+
text: "",
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** Append a chunk and return the current snapshot. */
|
|
32
|
+
push(chunk) {
|
|
33
|
+
this.buffer += chunk;
|
|
34
|
+
try {
|
|
35
|
+
const result = (0, parse_js_1.parseWithMeta)(this.buffer, this.options);
|
|
36
|
+
this.snapshot = { ...result, text: this.buffer };
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Malformed so far. Keep the last good snapshot; a later chunk may fix it.
|
|
40
|
+
this.snapshot = { ...this.snapshot, text: this.buffer };
|
|
41
|
+
}
|
|
42
|
+
return this.snapshot;
|
|
43
|
+
}
|
|
44
|
+
/** Latest best-effort value. */
|
|
45
|
+
get value() {
|
|
46
|
+
return this.snapshot.value;
|
|
47
|
+
}
|
|
48
|
+
/** `true` once a whole JSON value has been received. */
|
|
49
|
+
get complete() {
|
|
50
|
+
return this.snapshot.complete;
|
|
51
|
+
}
|
|
52
|
+
/** Path of the node still being written, or `null`. */
|
|
53
|
+
get incompletePath() {
|
|
54
|
+
return this.snapshot.incompletePath;
|
|
55
|
+
}
|
|
56
|
+
/** Everything received so far. */
|
|
57
|
+
get text() {
|
|
58
|
+
return this.buffer;
|
|
59
|
+
}
|
|
60
|
+
/** Clear all state so the instance can be reused. */
|
|
61
|
+
reset() {
|
|
62
|
+
this.buffer = "";
|
|
63
|
+
this.snapshot = {
|
|
64
|
+
value: undefined,
|
|
65
|
+
complete: false,
|
|
66
|
+
incompletePath: null,
|
|
67
|
+
text: "",
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.PartialJson = PartialJson;
|
|
72
|
+
/**
|
|
73
|
+
* Turn a stream of text chunks into a stream of progressively complete values.
|
|
74
|
+
*
|
|
75
|
+
* ```ts
|
|
76
|
+
* for await (const { value, complete } of parseStream(tokens)) {
|
|
77
|
+
* render(value)
|
|
78
|
+
* }
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
async function* parseStream(source, options = {}) {
|
|
82
|
+
const { onlyOnChange = true, ...parseOptions } = options;
|
|
83
|
+
const accumulator = new PartialJson(parseOptions);
|
|
84
|
+
let previous;
|
|
85
|
+
for await (const chunk of source) {
|
|
86
|
+
const snapshot = accumulator.push(chunk);
|
|
87
|
+
if (onlyOnChange) {
|
|
88
|
+
// Reaching completion counts as a change even when the value is
|
|
89
|
+
// already final, so consumers always see a `complete: true` snapshot.
|
|
90
|
+
const fingerprint = `${snapshot.complete ? "1" : "0"}:${JSON.stringify(snapshot.value ?? null)}`;
|
|
91
|
+
if (fingerprint === previous)
|
|
92
|
+
continue;
|
|
93
|
+
previous = fingerprint;
|
|
94
|
+
}
|
|
95
|
+
yield snapshot;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/stream.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAkGH,kCAqBC;AArHD,yCAA0C;AAQ1C;;;;;;;;;;GAUG;AACH,MAAa,WAAW;IAKvB,YAAY,UAAwB,EAAE;QAJ9B,WAAM,GAAG,EAAE,CAAA;QAKlB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,QAAQ,GAAG;YACf,KAAK,EAAE,SAAS;YAChB,QAAQ,EAAE,KAAK;YACf,cAAc,EAAE,IAAI;YACpB,IAAI,EAAE,EAAE;SACR,CAAA;IACF,CAAC;IAED,sDAAsD;IACtD,IAAI,CAAC,KAAa;QACjB,IAAI,CAAC,MAAM,IAAI,KAAK,CAAA;QACpB,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,IAAA,wBAAa,EAAI,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;YAC1D,IAAI,CAAC,QAAQ,GAAG,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,CAAA;QACjD,CAAC;QAAC,MAAM,CAAC;YACR,2EAA2E;YAC3E,IAAI,CAAC,QAAQ,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,CAAA;QACxD,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAA;IACrB,CAAC;IAED,gCAAgC;IAChC,IAAI,KAAK;QACR,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAA;IAC3B,CAAC;IAED,wDAAwD;IACxD,IAAI,QAAQ;QACX,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAA;IAC9B,CAAC;IAED,uDAAuD;IACvD,IAAI,cAAc;QACjB,OAAO,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAA;IACpC,CAAC;IAED,kCAAkC;IAClC,IAAI,IAAI;QACP,OAAO,IAAI,CAAC,MAAM,CAAA;IACnB,CAAC;IAED,qDAAqD;IACrD,KAAK;QACJ,IAAI,CAAC,MAAM,GAAG,EAAE,CAAA;QAChB,IAAI,CAAC,QAAQ,GAAG;YACf,KAAK,EAAE,SAAS;YAChB,QAAQ,EAAE,KAAK;YACf,cAAc,EAAE,IAAI;YACpB,IAAI,EAAE,EAAE;SACR,CAAA;IACF,CAAC;CACD;AA1DD,kCA0DC;AAUD;;;;;;;;GAQG;AACI,KAAK,SAAS,CAAC,CAAC,WAAW,CACjC,MAAgD,EAChD,UAAyB,EAAE;IAE3B,MAAM,EAAE,YAAY,GAAG,IAAI,EAAE,GAAG,YAAY,EAAE,GAAG,OAAO,CAAA;IACxD,MAAM,WAAW,GAAG,IAAI,WAAW,CAAI,YAAY,CAAC,CAAA;IACpD,IAAI,QAA4B,CAAA;IAEhC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAA+B,EAAE,CAAC;QAC3D,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACxC,IAAI,YAAY,EAAE,CAAC;YAClB,gEAAgE;YAChE,sEAAsE;YACtE,MAAM,WAAW,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,SAAS,CACrE,QAAQ,CAAC,KAAK,IAAI,IAAI,CACtB,EAAE,CAAA;YACH,IAAI,WAAW,KAAK,QAAQ;gBAAE,SAAQ;YACtC,QAAQ,GAAG,WAAW,CAAA;QACvB,CAAC;QACD,MAAM,QAAQ,CAAA;IACf,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* unclosed — parse JSON that has not finished arriving yet.
|
|
3
|
+
*
|
|
4
|
+
* Zero dependencies, one job, frozen API.
|
|
5
|
+
*/
|
|
6
|
+
export { parse, parseWithMeta, tryParse, isComplete, repair, scanToJson, UnclosedError, } from "./parse.js";
|
|
7
|
+
export { PartialJson, parseStream } from "./stream.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACN,KAAK,EACL,aAAa,EACb,QAAQ,EACR,UAAU,EACV,MAAM,EACN,UAAU,EACV,aAAa,GACb,MAAM,YAAY,CAAA;AAInB,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tolerant JSON parser for incomplete ("unclosed") input.
|
|
3
|
+
*
|
|
4
|
+
* The parser reads as much of a JSON document as is present and closes any
|
|
5
|
+
* containers that were still open when the input ended. It never guesses at
|
|
6
|
+
* data it has not seen: a truncated object key, a half-written escape sequence
|
|
7
|
+
* or an unrecoverable number is dropped rather than invented.
|
|
8
|
+
*
|
|
9
|
+
* Zero dependencies. No configuration files. No runtime requirements beyond
|
|
10
|
+
* ES2020.
|
|
11
|
+
*/
|
|
12
|
+
/** Thrown for input that is malformed rather than merely unfinished. */
|
|
13
|
+
export class UnclosedError extends SyntaxError {
|
|
14
|
+
constructor(message, position) {
|
|
15
|
+
super(`${message} (at position ${position})`);
|
|
16
|
+
this.name = "UnclosedError";
|
|
17
|
+
this.position = position;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const MISSING = Symbol("unclosed.missing");
|
|
21
|
+
const DEFAULTS = {
|
|
22
|
+
partialStrings: true,
|
|
23
|
+
partialLiterals: true,
|
|
24
|
+
scan: true,
|
|
25
|
+
};
|
|
26
|
+
const LITERALS = [
|
|
27
|
+
["true", true],
|
|
28
|
+
["false", false],
|
|
29
|
+
["null", null],
|
|
30
|
+
];
|
|
31
|
+
function isDigit(c) {
|
|
32
|
+
return c >= "0" && c <= "9";
|
|
33
|
+
}
|
|
34
|
+
function isHex(s) {
|
|
35
|
+
for (let i = 0; i < s.length; i++) {
|
|
36
|
+
const c = s[i].toLowerCase();
|
|
37
|
+
if (!(isDigit(c) || (c >= "a" && c <= "f")))
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Strip Markdown fences and leading prose so that model output such as
|
|
44
|
+
* "Sure! ```json\n{..." parses without pre-processing on the caller's side.
|
|
45
|
+
*/
|
|
46
|
+
export function scanToJson(input) {
|
|
47
|
+
let text = input;
|
|
48
|
+
const fence = /```[A-Za-z0-9_-]*[ \t]*\r?\n?/.exec(text);
|
|
49
|
+
if (fence) {
|
|
50
|
+
text = text.slice(fence.index + fence[0].length);
|
|
51
|
+
const close = text.indexOf("```");
|
|
52
|
+
if (close !== -1)
|
|
53
|
+
text = text.slice(0, close);
|
|
54
|
+
}
|
|
55
|
+
const start = text.search(/[[{]/);
|
|
56
|
+
if (start > 0)
|
|
57
|
+
text = text.slice(start);
|
|
58
|
+
return text;
|
|
59
|
+
}
|
|
60
|
+
class Parser {
|
|
61
|
+
constructor(src, opts) {
|
|
62
|
+
this.pos = 0;
|
|
63
|
+
this.path = [];
|
|
64
|
+
this.complete = true;
|
|
65
|
+
this.incompletePath = null;
|
|
66
|
+
this.src = src;
|
|
67
|
+
this.opts = opts;
|
|
68
|
+
}
|
|
69
|
+
get atEnd() {
|
|
70
|
+
return this.pos >= this.src.length;
|
|
71
|
+
}
|
|
72
|
+
/** Record that input ran out while reading the current node. */
|
|
73
|
+
truncated() {
|
|
74
|
+
if (this.complete) {
|
|
75
|
+
this.complete = false;
|
|
76
|
+
this.incompletePath = this.path.slice();
|
|
77
|
+
}
|
|
78
|
+
this.pos = this.src.length;
|
|
79
|
+
}
|
|
80
|
+
fail(message) {
|
|
81
|
+
throw new UnclosedError(message, this.pos);
|
|
82
|
+
}
|
|
83
|
+
/** Whitespace, plus `//` and block comments, which models emit often. */
|
|
84
|
+
skip() {
|
|
85
|
+
const s = this.src;
|
|
86
|
+
while (this.pos < s.length) {
|
|
87
|
+
const c = s[this.pos];
|
|
88
|
+
if (c === " " || c === "\n" || c === "\r" || c === "\t" || c === "\uFEFF") {
|
|
89
|
+
this.pos++;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (c === "/" && s[this.pos + 1] === "/") {
|
|
93
|
+
this.pos += 2;
|
|
94
|
+
while (this.pos < s.length && s[this.pos] !== "\n")
|
|
95
|
+
this.pos++;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (c === "/" && s[this.pos + 1] === "*") {
|
|
99
|
+
this.pos += 2;
|
|
100
|
+
while (this.pos < s.length &&
|
|
101
|
+
!(s[this.pos] === "*" && s[this.pos + 1] === "/")) {
|
|
102
|
+
this.pos++;
|
|
103
|
+
}
|
|
104
|
+
this.pos = Math.min(this.pos + 2, s.length);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
parse() {
|
|
111
|
+
const value = this.value();
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
value() {
|
|
115
|
+
this.skip();
|
|
116
|
+
if (this.atEnd) {
|
|
117
|
+
this.truncated();
|
|
118
|
+
return MISSING;
|
|
119
|
+
}
|
|
120
|
+
const c = this.src[this.pos];
|
|
121
|
+
if (c === "{")
|
|
122
|
+
return this.object();
|
|
123
|
+
if (c === "[")
|
|
124
|
+
return this.array();
|
|
125
|
+
if (c === '"' || c === "'")
|
|
126
|
+
return this.string(c);
|
|
127
|
+
if (c === "-" || c === "+" || isDigit(c))
|
|
128
|
+
return this.number();
|
|
129
|
+
if (/[A-Za-z]/.test(c))
|
|
130
|
+
return this.literal();
|
|
131
|
+
return this.fail(`Unexpected character ${JSON.stringify(c)}`);
|
|
132
|
+
}
|
|
133
|
+
object() {
|
|
134
|
+
this.pos++;
|
|
135
|
+
const out = {};
|
|
136
|
+
for (;;) {
|
|
137
|
+
this.skip();
|
|
138
|
+
if (this.atEnd) {
|
|
139
|
+
this.truncated();
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
const c = this.src[this.pos];
|
|
143
|
+
if (c === "}") {
|
|
144
|
+
this.pos++;
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
if (c === ",") {
|
|
148
|
+
this.pos++;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (c !== '"' && c !== "'") {
|
|
152
|
+
return this.fail(`Expected a key but found ${JSON.stringify(c)}`);
|
|
153
|
+
}
|
|
154
|
+
const key = this.string(c);
|
|
155
|
+
// A key that is still being written tells us nothing about its value.
|
|
156
|
+
if (key === MISSING || !this.complete)
|
|
157
|
+
return out;
|
|
158
|
+
this.skip();
|
|
159
|
+
if (this.atEnd) {
|
|
160
|
+
this.truncated();
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
const sep = this.src[this.pos];
|
|
164
|
+
if (sep === ":") {
|
|
165
|
+
this.pos++;
|
|
166
|
+
}
|
|
167
|
+
else if (sep === "," || sep === "}") {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
return this.fail(`Expected ':' but found ${JSON.stringify(sep)}`);
|
|
172
|
+
}
|
|
173
|
+
this.skip();
|
|
174
|
+
if (this.atEnd) {
|
|
175
|
+
this.truncated();
|
|
176
|
+
return out;
|
|
177
|
+
}
|
|
178
|
+
this.path.push(key);
|
|
179
|
+
const value = this.value();
|
|
180
|
+
this.path.pop();
|
|
181
|
+
if (value === MISSING)
|
|
182
|
+
return out;
|
|
183
|
+
out[key] = value;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
array() {
|
|
187
|
+
this.pos++;
|
|
188
|
+
const out = [];
|
|
189
|
+
for (;;) {
|
|
190
|
+
this.skip();
|
|
191
|
+
if (this.atEnd) {
|
|
192
|
+
this.truncated();
|
|
193
|
+
return out;
|
|
194
|
+
}
|
|
195
|
+
const c = this.src[this.pos];
|
|
196
|
+
if (c === "]") {
|
|
197
|
+
this.pos++;
|
|
198
|
+
return out;
|
|
199
|
+
}
|
|
200
|
+
if (c === ",") {
|
|
201
|
+
this.pos++;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
this.path.push(out.length);
|
|
205
|
+
const value = this.value();
|
|
206
|
+
this.path.pop();
|
|
207
|
+
if (value === MISSING)
|
|
208
|
+
return out;
|
|
209
|
+
out.push(value);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
string(quote) {
|
|
213
|
+
const s = this.src;
|
|
214
|
+
this.pos++;
|
|
215
|
+
let out = "";
|
|
216
|
+
while (this.pos < s.length) {
|
|
217
|
+
const c = s[this.pos];
|
|
218
|
+
if (c === quote) {
|
|
219
|
+
this.pos++;
|
|
220
|
+
return out;
|
|
221
|
+
}
|
|
222
|
+
if (c !== "\\") {
|
|
223
|
+
out += c;
|
|
224
|
+
this.pos++;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
// Escape sequence. If it is cut in half, drop it and stop here so we
|
|
228
|
+
// never emit a character the sender did not finish writing.
|
|
229
|
+
if (this.pos + 1 >= s.length)
|
|
230
|
+
return this.unterminated(out);
|
|
231
|
+
const esc = s[this.pos + 1];
|
|
232
|
+
if (esc === "u") {
|
|
233
|
+
const hex = s.slice(this.pos + 2, this.pos + 6);
|
|
234
|
+
if (hex.length < 4)
|
|
235
|
+
return this.unterminated(out);
|
|
236
|
+
if (!isHex(hex)) {
|
|
237
|
+
this.pos += 1;
|
|
238
|
+
return this.fail(`Invalid unicode escape \\u${hex}`);
|
|
239
|
+
}
|
|
240
|
+
out += String.fromCharCode(parseInt(hex, 16));
|
|
241
|
+
this.pos += 6;
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
const simple = SIMPLE_ESCAPES[esc];
|
|
245
|
+
if (simple === undefined) {
|
|
246
|
+
this.pos += 1;
|
|
247
|
+
return this.fail(`Invalid escape \\${esc}`);
|
|
248
|
+
}
|
|
249
|
+
out += simple;
|
|
250
|
+
this.pos += 2;
|
|
251
|
+
}
|
|
252
|
+
return this.unterminated(out);
|
|
253
|
+
}
|
|
254
|
+
unterminated(sofar) {
|
|
255
|
+
this.truncated();
|
|
256
|
+
return this.opts.partialStrings ? sofar : MISSING;
|
|
257
|
+
}
|
|
258
|
+
number() {
|
|
259
|
+
const s = this.src;
|
|
260
|
+
const start = this.pos;
|
|
261
|
+
if (s[this.pos] === "-" || s[this.pos] === "+")
|
|
262
|
+
this.pos++;
|
|
263
|
+
while (this.pos < s.length && isDigit(s[this.pos]))
|
|
264
|
+
this.pos++;
|
|
265
|
+
if (s[this.pos] === ".") {
|
|
266
|
+
this.pos++;
|
|
267
|
+
while (this.pos < s.length && isDigit(s[this.pos]))
|
|
268
|
+
this.pos++;
|
|
269
|
+
}
|
|
270
|
+
if (s[this.pos] === "e" || s[this.pos] === "E") {
|
|
271
|
+
this.pos++;
|
|
272
|
+
if (s[this.pos] === "+" || s[this.pos] === "-")
|
|
273
|
+
this.pos++;
|
|
274
|
+
while (this.pos < s.length && isDigit(s[this.pos]))
|
|
275
|
+
this.pos++;
|
|
276
|
+
}
|
|
277
|
+
let raw = s.slice(start, this.pos);
|
|
278
|
+
if (this.atEnd) {
|
|
279
|
+
// A number touching the end of the buffer may still be growing.
|
|
280
|
+
this.truncated();
|
|
281
|
+
raw = raw.replace(/[.eE][+-]?$/, "").replace(/[.eE]$/, "");
|
|
282
|
+
}
|
|
283
|
+
if (!/^[+-]?[0-9]/.test(raw))
|
|
284
|
+
return MISSING;
|
|
285
|
+
const n = Number(raw);
|
|
286
|
+
return Number.isFinite(n) ? n : MISSING;
|
|
287
|
+
}
|
|
288
|
+
literal() {
|
|
289
|
+
const rest = this.src.slice(this.pos);
|
|
290
|
+
for (const [word, value] of LITERALS) {
|
|
291
|
+
if (rest.startsWith(word)) {
|
|
292
|
+
this.pos += word.length;
|
|
293
|
+
return value;
|
|
294
|
+
}
|
|
295
|
+
if (rest.length > 0 && word.startsWith(rest)) {
|
|
296
|
+
this.truncated();
|
|
297
|
+
return this.opts.partialLiterals ? value : MISSING;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return this.fail(`Unexpected token ${JSON.stringify(rest.slice(0, 8))}`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
const SIMPLE_ESCAPES = {
|
|
304
|
+
'"': '"',
|
|
305
|
+
"'": "'",
|
|
306
|
+
"\\": "\\",
|
|
307
|
+
"/": "/",
|
|
308
|
+
b: "\b",
|
|
309
|
+
f: "\f",
|
|
310
|
+
n: "\n",
|
|
311
|
+
r: "\r",
|
|
312
|
+
t: "\t",
|
|
313
|
+
};
|
|
314
|
+
/**
|
|
315
|
+
* Parse possibly-incomplete JSON and report how much of it is final.
|
|
316
|
+
*
|
|
317
|
+
* @throws {UnclosedError} when the input is malformed rather than unfinished.
|
|
318
|
+
*/
|
|
319
|
+
export function parseWithMeta(input, options = {}) {
|
|
320
|
+
const opts = { ...DEFAULTS, ...options };
|
|
321
|
+
const text = opts.scan ? scanToJson(input) : input;
|
|
322
|
+
const parser = new Parser(text, opts);
|
|
323
|
+
const value = parser.parse();
|
|
324
|
+
return {
|
|
325
|
+
value: value === MISSING ? undefined : value,
|
|
326
|
+
complete: parser.complete,
|
|
327
|
+
incompletePath: parser.incompletePath,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Parse possibly-incomplete JSON.
|
|
332
|
+
*
|
|
333
|
+
* Returns `undefined` when no value could be recovered at all.
|
|
334
|
+
*
|
|
335
|
+
* @throws {UnclosedError} when the input is malformed rather than unfinished.
|
|
336
|
+
*/
|
|
337
|
+
export function parse(input, options) {
|
|
338
|
+
return parseWithMeta(input, options).value;
|
|
339
|
+
}
|
|
340
|
+
/** Parse without throwing. Returns `undefined` on malformed input. */
|
|
341
|
+
export function tryParse(input, options) {
|
|
342
|
+
try {
|
|
343
|
+
return parse(input, options);
|
|
344
|
+
}
|
|
345
|
+
catch {
|
|
346
|
+
return undefined;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
/** `true` when the input holds one whole, untruncated JSON value. */
|
|
350
|
+
export function isComplete(input, options) {
|
|
351
|
+
try {
|
|
352
|
+
return parseWithMeta(input, options).complete;
|
|
353
|
+
}
|
|
354
|
+
catch {
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Turn possibly-incomplete JSON into a valid JSON string.
|
|
360
|
+
*
|
|
361
|
+
* @throws {UnclosedError} when the input is malformed rather than unfinished.
|
|
362
|
+
*/
|
|
363
|
+
export function repair(input, options) {
|
|
364
|
+
const { value } = parseWithMeta(input, options);
|
|
365
|
+
return JSON.stringify(value === undefined ? null : value);
|
|
366
|
+
}
|
|
367
|
+
//# sourceMappingURL=parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../../src/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAyCH,wEAAwE;AACxE,MAAM,OAAO,aAAc,SAAQ,WAAW;IAE7C,YAAY,OAAe,EAAE,QAAgB;QAC5C,KAAK,CAAC,GAAG,OAAO,iBAAiB,QAAQ,GAAG,CAAC,CAAA;QAC7C,IAAI,CAAC,IAAI,GAAG,eAAe,CAAA;QAC3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;IACzB,CAAC;CACD;AAED,MAAM,OAAO,GAAG,MAAM,CAAC,kBAAkB,CAAC,CAAA;AAG1C,MAAM,QAAQ,GAA2B;IACxC,cAAc,EAAE,IAAI;IACpB,eAAe,EAAE,IAAI;IACrB,IAAI,EAAE,IAAI;CACV,CAAA;AAED,MAAM,QAAQ,GAA2C;IACxD,CAAC,MAAM,EAAE,IAAI,CAAC;IACd,CAAC,OAAO,EAAE,KAAK,CAAC;IAChB,CAAC,MAAM,EAAE,IAAI,CAAC;CACd,CAAA;AAED,SAAS,OAAO,CAAC,CAAS;IACzB,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,CAAA;AAC5B,CAAC;AAED,SAAS,KAAK,CAAC,CAAS;IACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,CAAA;QAC7B,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;YAAE,OAAO,KAAK,CAAA;IAC1D,CAAC;IACD,OAAO,IAAI,CAAA;AACZ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,KAAa;IACvC,IAAI,IAAI,GAAG,KAAK,CAAA;IAChB,MAAM,KAAK,GAAG,+BAA+B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACxD,IAAI,KAAK,EAAE,CAAC;QACX,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;QAChD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QACjC,IAAI,KAAK,KAAK,CAAC,CAAC;YAAE,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;IAC9C,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACjC,IAAI,KAAK,GAAG,CAAC;QAAE,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IACvC,OAAO,IAAI,CAAA;AACZ,CAAC;AAED,MAAM,MAAM;IAQX,YAAY,GAAW,EAAE,IAA4B;QAL7C,QAAG,GAAG,CAAC,CAAA;QACP,SAAI,GAA2B,EAAE,CAAA;QACzC,aAAQ,GAAG,IAAI,CAAA;QACf,mBAAc,GAAkC,IAAI,CAAA;QAGnD,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IACjB,CAAC;IAED,IAAY,KAAK;QAChB,OAAO,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAA;IACnC,CAAC;IAED,gEAAgE;IACxD,SAAS;QAChB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAA;YACrB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAA;QACxC,CAAC;QACD,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAA;IAC3B,CAAC;IAEO,IAAI,CAAC,OAAe;QAC3B,MAAM,IAAI,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;IAC3C,CAAC;IAED,yEAAyE;IACjE,IAAI;QACX,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAA;QAClB,OAAO,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;YAC5B,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAE,CAAA;YACtB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,QAAQ,EAAE,CAAC;gBAC3E,IAAI,CAAC,GAAG,EAAE,CAAA;gBACV,SAAQ;YACT,CAAC;YACD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBAC1C,IAAI,CAAC,GAAG,IAAI,CAAC,CAAA;gBACb,OAAO,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI;oBAAE,IAAI,CAAC,GAAG,EAAE,CAAA;gBAC9D,SAAQ;YACT,CAAC;YACD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBAC1C,IAAI,CAAC,GAAG,IAAI,CAAC,CAAA;gBACb,OACC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM;oBACnB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC,EAChD,CAAC;oBACF,IAAI,CAAC,GAAG,EAAE,CAAA;gBACX,CAAC;gBACD,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAA;gBAC3C,SAAQ;YACT,CAAC;YACD,MAAK;QACN,CAAC;IACF,CAAC;IAED,KAAK;QACJ,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAA;QAC1B,OAAO,KAAK,CAAA;IACb,CAAC;IAEO,KAAK;QACZ,IAAI,CAAC,IAAI,EAAE,CAAA;QACX,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,SAAS,EAAE,CAAA;YAChB,OAAO,OAAO,CAAA;QACf,CAAC;QACD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAE,CAAA;QAC7B,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC,MAAM,EAAE,CAAA;QACnC,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC,KAAK,EAAE,CAAA;QAClC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;QACjD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC,MAAM,EAAE,CAAA;QAC9D,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC,OAAO,EAAE,CAAA;QAC7C,OAAO,IAAI,CAAC,IAAI,CAAC,wBAAwB,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAC9D,CAAC;IAEO,MAAM;QACb,IAAI,CAAC,GAAG,EAAE,CAAA;QACV,MAAM,GAAG,GAAyB,EAAE,CAAA;QACpC,SAAS,CAAC;YACT,IAAI,CAAC,IAAI,EAAE,CAAA;YACX,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBAChB,IAAI,CAAC,SAAS,EAAE,CAAA;gBAChB,OAAO,GAAG,CAAA;YACX,CAAC;YACD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAE,CAAA;YAC7B,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;gBACf,IAAI,CAAC,GAAG,EAAE,CAAA;gBACV,OAAO,GAAG,CAAA;YACX,CAAC;YACD,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;gBACf,IAAI,CAAC,GAAG,EAAE,CAAA;gBACV,SAAQ;YACT,CAAC;YACD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;gBAC5B,OAAO,IAAI,CAAC,IAAI,CAAC,4BAA4B,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;YAClE,CAAC;YACD,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;YAC1B,sEAAsE;YACtE,IAAI,GAAG,KAAK,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ;gBAAE,OAAO,GAAG,CAAA;YAEjD,IAAI,CAAC,IAAI,EAAE,CAAA;YACX,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBAChB,IAAI,CAAC,SAAS,EAAE,CAAA;gBAChB,OAAO,GAAG,CAAA;YACX,CAAC;YACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAE,CAAA;YAC/B,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;gBACjB,IAAI,CAAC,GAAG,EAAE,CAAA;YACX,CAAC;iBAAM,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;gBACvC,SAAQ;YACT,CAAC;iBAAM,CAAC;gBACP,OAAO,IAAI,CAAC,IAAI,CAAC,0BAA0B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YAClE,CAAC;YAED,IAAI,CAAC,IAAI,EAAE,CAAA;YACX,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBAChB,IAAI,CAAC,SAAS,EAAE,CAAA;gBAChB,OAAO,GAAG,CAAA;YACX,CAAC;YAED,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;YACnB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAA;YAC1B,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAA;YACf,IAAI,KAAK,KAAK,OAAO;gBAAE,OAAO,GAAG,CAAA;YACjC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;QACjB,CAAC;IACF,CAAC;IAEO,KAAK;QACZ,IAAI,CAAC,GAAG,EAAE,CAAA;QACV,MAAM,GAAG,GAAW,EAAE,CAAA;QACtB,SAAS,CAAC;YACT,IAAI,CAAC,IAAI,EAAE,CAAA;YACX,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBAChB,IAAI,CAAC,SAAS,EAAE,CAAA;gBAChB,OAAO,GAAG,CAAA;YACX,CAAC;YACD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAE,CAAA;YAC7B,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;gBACf,IAAI,CAAC,GAAG,EAAE,CAAA;gBACV,OAAO,GAAG,CAAA;YACX,CAAC;YACD,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;gBACf,IAAI,CAAC,GAAG,EAAE,CAAA;gBACV,SAAQ;YACT,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;YAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAA;YAC1B,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAA;YACf,IAAI,KAAK,KAAK,OAAO;gBAAE,OAAO,GAAG,CAAA;YACjC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAChB,CAAC;IACF,CAAC;IAEO,MAAM,CAAC,KAAa;QAC3B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAA;QAClB,IAAI,CAAC,GAAG,EAAE,CAAA;QACV,IAAI,GAAG,GAAG,EAAE,CAAA;QACZ,OAAO,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;YAC5B,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAE,CAAA;YACtB,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC;gBACjB,IAAI,CAAC,GAAG,EAAE,CAAA;gBACV,OAAO,GAAG,CAAA;YACX,CAAC;YACD,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;gBAChB,GAAG,IAAI,CAAC,CAAA;gBACR,IAAI,CAAC,GAAG,EAAE,CAAA;gBACV,SAAQ;YACT,CAAC;YACD,qEAAqE;YACrE,4DAA4D;YAC5D,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAA;YAC3D,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAE,CAAA;YAC5B,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;gBACjB,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAA;gBAC/C,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;oBAAE,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAA;gBACjD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;oBACjB,IAAI,CAAC,GAAG,IAAI,CAAC,CAAA;oBACb,OAAO,IAAI,CAAC,IAAI,CAAC,6BAA6B,GAAG,EAAE,CAAC,CAAA;gBACrD,CAAC;gBACD,GAAG,IAAI,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAA;gBAC7C,IAAI,CAAC,GAAG,IAAI,CAAC,CAAA;gBACb,SAAQ;YACT,CAAC;YACD,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,CAAA;YAClC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC1B,IAAI,CAAC,GAAG,IAAI,CAAC,CAAA;gBACb,OAAO,IAAI,CAAC,IAAI,CAAC,oBAAoB,GAAG,EAAE,CAAC,CAAA;YAC5C,CAAC;YACD,GAAG,IAAI,MAAM,CAAA;YACb,IAAI,CAAC,GAAG,IAAI,CAAC,CAAA;QACd,CAAC;QACD,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAA;IAC9B,CAAC;IAEO,YAAY,CAAC,KAAa;QACjC,IAAI,CAAC,SAAS,EAAE,CAAA;QAChB,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAA;IAClD,CAAC;IAEO,MAAM;QACb,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAA;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAA;QACtB,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG;YAAE,IAAI,CAAC,GAAG,EAAE,CAAA;QAC1D,OAAO,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAE,CAAC;YAAE,IAAI,CAAC,GAAG,EAAE,CAAA;QAC/D,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,EAAE,CAAC;YACzB,IAAI,CAAC,GAAG,EAAE,CAAA;YACV,OAAO,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAE,CAAC;gBAAE,IAAI,CAAC,GAAG,EAAE,CAAA;QAChE,CAAC;QACD,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,EAAE,CAAC;YAChD,IAAI,CAAC,GAAG,EAAE,CAAA;YACV,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG;gBAAE,IAAI,CAAC,GAAG,EAAE,CAAA;YAC1D,OAAO,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAE,CAAC;gBAAE,IAAI,CAAC,GAAG,EAAE,CAAA;QAChE,CAAC;QACD,IAAI,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;QAClC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAChB,gEAAgE;YAChE,IAAI,CAAC,SAAS,EAAE,CAAA;YAChB,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAA;QAC3D,CAAC;QACD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,OAAO,CAAA;QAC5C,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;QACrB,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;IACxC,CAAC;IAEO,OAAO;QACd,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACrC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,QAAQ,EAAE,CAAC;YACtC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC3B,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,MAAM,CAAA;gBACvB,OAAO,KAAK,CAAA;YACb,CAAC;YACD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9C,IAAI,CAAC,SAAS,EAAE,CAAA;gBAChB,OAAO,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAA;YACnD,CAAC;QACF,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAC,oBAAoB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IACzE,CAAC;CACD;AAED,MAAM,cAAc,GAAuC;IAC1D,GAAG,EAAE,GAAG;IACR,GAAG,EAAE,GAAG;IACR,IAAI,EAAE,IAAI;IACV,GAAG,EAAE,GAAG;IACR,CAAC,EAAE,IAAI;IACP,CAAC,EAAE,IAAI;IACP,CAAC,EAAE,IAAI;IACP,CAAC,EAAE,IAAI;IACP,CAAC,EAAE,IAAI;CACP,CAAA;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAC5B,KAAa,EACb,UAAwB,EAAE;IAE1B,MAAM,IAAI,GAAG,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,CAAA;IACxC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IAClD,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IACrC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,EAAE,CAAA;IAC5B,OAAO;QACN,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAE,KAAW;QACnD,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,cAAc,EAAE,MAAM,CAAC,cAAc;KACrC,CAAA;AACF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,KAAK,CACpB,KAAa,EACb,OAAsB;IAEtB,OAAO,aAAa,CAAI,KAAK,EAAE,OAAO,CAAC,CAAC,KAAK,CAAA;AAC9C,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,QAAQ,CACvB,KAAa,EACb,OAAsB;IAEtB,IAAI,CAAC;QACJ,OAAO,KAAK,CAAI,KAAK,EAAE,OAAO,CAAC,CAAA;IAChC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,SAAS,CAAA;IACjB,CAAC;AACF,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,UAAU,CAAC,KAAa,EAAE,OAAsB;IAC/D,IAAI,CAAC;QACJ,OAAO,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAA;IAC9C,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAA;IACb,CAAC;AACF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAC,KAAa,EAAE,OAAsB;IAC3D,MAAM,EAAE,KAAK,EAAE,GAAG,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IAC/C,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;AAC1D,CAAC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Incremental helpers for feeding model output in as it arrives.
|
|
3
|
+
*/
|
|
4
|
+
import { parseWithMeta } from "./parse.js";
|
|
5
|
+
/**
|
|
6
|
+
* Accumulates chunks and exposes the best-effort value after each one.
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* const acc = new PartialJson()
|
|
10
|
+
* for await (const chunk of tokens) {
|
|
11
|
+
* const snapshot = acc.push(chunk)
|
|
12
|
+
* render(snapshot.value)
|
|
13
|
+
* }
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export class PartialJson {
|
|
17
|
+
constructor(options = {}) {
|
|
18
|
+
this.buffer = "";
|
|
19
|
+
this.options = options;
|
|
20
|
+
this.snapshot = {
|
|
21
|
+
value: undefined,
|
|
22
|
+
complete: false,
|
|
23
|
+
incompletePath: null,
|
|
24
|
+
text: "",
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** Append a chunk and return the current snapshot. */
|
|
28
|
+
push(chunk) {
|
|
29
|
+
this.buffer += chunk;
|
|
30
|
+
try {
|
|
31
|
+
const result = parseWithMeta(this.buffer, this.options);
|
|
32
|
+
this.snapshot = { ...result, text: this.buffer };
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Malformed so far. Keep the last good snapshot; a later chunk may fix it.
|
|
36
|
+
this.snapshot = { ...this.snapshot, text: this.buffer };
|
|
37
|
+
}
|
|
38
|
+
return this.snapshot;
|
|
39
|
+
}
|
|
40
|
+
/** Latest best-effort value. */
|
|
41
|
+
get value() {
|
|
42
|
+
return this.snapshot.value;
|
|
43
|
+
}
|
|
44
|
+
/** `true` once a whole JSON value has been received. */
|
|
45
|
+
get complete() {
|
|
46
|
+
return this.snapshot.complete;
|
|
47
|
+
}
|
|
48
|
+
/** Path of the node still being written, or `null`. */
|
|
49
|
+
get incompletePath() {
|
|
50
|
+
return this.snapshot.incompletePath;
|
|
51
|
+
}
|
|
52
|
+
/** Everything received so far. */
|
|
53
|
+
get text() {
|
|
54
|
+
return this.buffer;
|
|
55
|
+
}
|
|
56
|
+
/** Clear all state so the instance can be reused. */
|
|
57
|
+
reset() {
|
|
58
|
+
this.buffer = "";
|
|
59
|
+
this.snapshot = {
|
|
60
|
+
value: undefined,
|
|
61
|
+
complete: false,
|
|
62
|
+
incompletePath: null,
|
|
63
|
+
text: "",
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Turn a stream of text chunks into a stream of progressively complete values.
|
|
69
|
+
*
|
|
70
|
+
* ```ts
|
|
71
|
+
* for await (const { value, complete } of parseStream(tokens)) {
|
|
72
|
+
* render(value)
|
|
73
|
+
* }
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export async function* parseStream(source, options = {}) {
|
|
77
|
+
const { onlyOnChange = true, ...parseOptions } = options;
|
|
78
|
+
const accumulator = new PartialJson(parseOptions);
|
|
79
|
+
let previous;
|
|
80
|
+
for await (const chunk of source) {
|
|
81
|
+
const snapshot = accumulator.push(chunk);
|
|
82
|
+
if (onlyOnChange) {
|
|
83
|
+
// Reaching completion counts as a change even when the value is
|
|
84
|
+
// already final, so consumers always see a `complete: true` snapshot.
|
|
85
|
+
const fingerprint = `${snapshot.complete ? "1" : "0"}:${JSON.stringify(snapshot.value ?? null)}`;
|
|
86
|
+
if (fingerprint === previous)
|
|
87
|
+
continue;
|
|
88
|
+
previous = fingerprint;
|
|
89
|
+
}
|
|
90
|
+
yield snapshot;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/stream.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAQ1C;;;;;;;;;;GAUG;AACH,MAAM,OAAO,WAAW;IAKvB,YAAY,UAAwB,EAAE;QAJ9B,WAAM,GAAG,EAAE,CAAA;QAKlB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,QAAQ,GAAG;YACf,KAAK,EAAE,SAAS;YAChB,QAAQ,EAAE,KAAK;YACf,cAAc,EAAE,IAAI;YACpB,IAAI,EAAE,EAAE;SACR,CAAA;IACF,CAAC;IAED,sDAAsD;IACtD,IAAI,CAAC,KAAa;QACjB,IAAI,CAAC,MAAM,IAAI,KAAK,CAAA;QACpB,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,aAAa,CAAI,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;YAC1D,IAAI,CAAC,QAAQ,GAAG,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,CAAA;QACjD,CAAC;QAAC,MAAM,CAAC;YACR,2EAA2E;YAC3E,IAAI,CAAC,QAAQ,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,CAAA;QACxD,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAA;IACrB,CAAC;IAED,gCAAgC;IAChC,IAAI,KAAK;QACR,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAA;IAC3B,CAAC;IAED,wDAAwD;IACxD,IAAI,QAAQ;QACX,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAA;IAC9B,CAAC;IAED,uDAAuD;IACvD,IAAI,cAAc;QACjB,OAAO,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAA;IACpC,CAAC;IAED,kCAAkC;IAClC,IAAI,IAAI;QACP,OAAO,IAAI,CAAC,MAAM,CAAA;IACnB,CAAC;IAED,qDAAqD;IACrD,KAAK;QACJ,IAAI,CAAC,MAAM,GAAG,EAAE,CAAA;QAChB,IAAI,CAAC,QAAQ,GAAG;YACf,KAAK,EAAE,SAAS;YAChB,QAAQ,EAAE,KAAK;YACf,cAAc,EAAE,IAAI;YACpB,IAAI,EAAE,EAAE;SACR,CAAA;IACF,CAAC;CACD;AAUD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,WAAW,CACjC,MAAgD,EAChD,UAAyB,EAAE;IAE3B,MAAM,EAAE,YAAY,GAAG,IAAI,EAAE,GAAG,YAAY,EAAE,GAAG,OAAO,CAAA;IACxD,MAAM,WAAW,GAAG,IAAI,WAAW,CAAI,YAAY,CAAC,CAAA;IACpD,IAAI,QAA4B,CAAA;IAEhC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAA+B,EAAE,CAAC;QAC3D,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACxC,IAAI,YAAY,EAAE,CAAC;YAClB,gEAAgE;YAChE,sEAAsE;YACtE,MAAM,WAAW,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,SAAS,CACrE,QAAQ,CAAC,KAAK,IAAI,IAAI,CACtB,EAAE,CAAA;YACH,IAAI,WAAW,KAAK,QAAQ;gBAAE,SAAQ;YACtC,QAAQ,GAAG,WAAW,CAAA;QACvB,CAAC;QACD,MAAM,QAAQ,CAAA;IACf,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* unclosed — parse JSON that has not finished arriving yet.
|
|
3
|
+
*
|
|
4
|
+
* Zero dependencies, one job, frozen API.
|
|
5
|
+
*/
|
|
6
|
+
export { parse, parseWithMeta, tryParse, isComplete, repair, scanToJson, UnclosedError, } from "./parse.js";
|
|
7
|
+
export type { Json, ParseOptions, ParseResult } from "./parse.js";
|
|
8
|
+
export { PartialJson, parseStream } from "./stream.js";
|
|
9
|
+
export type { StreamOptions, StreamSnapshot } from "./stream.js";
|