UNPKG

@jswalden/streaming-json

Version:

Streaming JSON parsing and stringification for JavaScript/TypeScript

169 lines (168 loc) 6.92 kB
/** * A type broadly characterizing all JSON-compatible objects that are not * arrays. */ export interface JSONObject { [key: string]: JSONValue | undefined; } /** A type broadly characterizing all JSON arrays. */ export type JSONArray = JSONValue[]; /** * A type broadly describing all values that can be serialized to JSON text. * * This type doesn't limit objects/arrays compatible with it to not have * getters/setters, to not be proxies with handler-implemented traps (or that * have been revoked), etc. It's just moderately nicer than using `any`. */ export type JSONValue = number | string | boolean | null | JSONArray | JSONObject; /** * The type of the optional `reviver` function that can be passed to * `StreamingJSONParser.finish`. */ export type Reviver<T> = (this: object, prop: string, val: any) => T; /** * A JSON parser when you wish to incrementally parse your JSON in fragments, * rather than from one complete string. * * Feed fragments of JSON text to the parser by calling `add(fragment)`. When * you've fed the entire JSON text to the parser, call `finish()` to get the * (optionally revived) result. * * If the concatenation of added fragments becomes not a prefix of valid JSON * text, or if the final concatenation of all fragments isn't valid JSON text, * the applicable `add(fragment)` or `finish()` will throw a `SyntaxError`. */ export declare class StreamingJSONParser { /** The current fragment being parsed. */ private fragment; /** The index within `this.fragment` of the next unexamined character. */ private current; /** The length of `this.fragment`. */ private end; /** Whether all fragments have been added. */ private eof; /** * Assuming `this.atEnd()`, wait for another fragment. If the sentinel "" * fragment is received, return `true`. Otherwise set it as the current * fragment and return `false` so that it can be parsed. */ private atEOF; /** Whether we've parsed to the end of the current fragment. */ private atEnd; /** * Consume whitespace until the current character isn't whitespace (or all * JSON text has been processed). */ private consumeWhitespace; /** Consume the given keyword starting from the current character. */ private consumeKeyword; /** Consume and return a JSON string, starting at its leading `"`. */ private jsonString; /** Consume and return a JSON number, starting at its leading digit or `-`. */ private jsonNumber; /** * Consume the (optional whitespace and) colon after a property name in an * object literal. */ private advanceColon; /** * Consume the (optional whitespace and) `,` or `}` after a property value in * an object literal. * * @returns `true` iff `}` was consumed and the object has ended */ private advanceObjectEnds; /** * Consume the (optional whitespace and) `,` or `]` after an element in an * array literal. * * @returns `true` iff `]` was consumed and the array has ended */ private advanceArrayEnds; /** The value of the atomic token consumed by an `advance`. */ private tokenValue; /** Advance and consume a token, in context where a JSON value is expected. */ private advance; /** * A generator for incrementally parsing a JSON text from nonempty fragments. * (Callers must filter out empty fragments manually if they choose to support * them.) * * After the implicit initial `yield` is passed by calling `.next()`, add the * nonempty fragments of JSON text using `.next(fragment)`. Indicate that all * fragments have been added using `.next("")`. * * If a `.next(fragment)` causes the concatenated fragments to not be a valid * prefix of JSON text, or if the concatenated fragments upon `.next("")` form * invalid JSON text, the pertinent `.next()` throws a `SyntaxError`. * * If upon `.next("")` the concatenated fragments form valid JSON text, that * call returns `{ done: true, value: <result of parsing the JSON text> }`. */ private parseJSON; private parser; private complete; constructor(); /** * Add a `fragment` of additional JSON text to the overall conceptual string * being parsed. * * @throws * `SyntaxError` if adding this fragment makes the concatenation of all * fragments not a valid prefix of JSON text. * @throws * `Error` (exactly, not a subclass) if fragments can't be added because a * previous `add(fragment)` already threw or because `finish()` was called. */ add(fragment: string): void; /** * Stop feeding fragments to this parser and compute and return the final * parse result. * * @throws * `Error` if parsing was already `done()`. * @throws * `SyntaxError` if the concatenation of all added fragments (which could be * the empty string if no fragments were added) isn't valid JSON. (It must * be the *prefix* of valid JSON text or a preceding `add(fragment)` would * have thrown.) * @returns * The overall parse result if the concatenated fragments constitute valid * JSON text. */ finish(): JSONValue; /** * Stop feeding fragments to this parser, and compute the final parse result * as the value `unfiltered`. * * Then apply `reviver` to `unfiltered` (and recursively to its properties and * elements) exactly as {@link JSON.parse} would do if it passed `reviver` and * the concatenation of fragments passed to this parser, and return the value * {@link JSON.parse} would return (which will be the result returned by the * outermost call of `reviver`). * * @throws * `Error` if parsing was already `done()`. * @throws * `SyntaxError` if the concatenation of all added fragments (which could be * the empty string if no fragments were added) isn't valid JSON. (It must * be the *prefix* of valid JSON text or a preceding `add(fragment)` would * have thrown.) * @throws * Any value that `reviver` throws during the reviving process. * @returns * The overall parse result if the concatenated fragments constitute valid * JSON text, as modified by `reviver`. */ finish<T>(reviver: Reviver<T>): T; /** * Return `true` if more fragments can be added to this parser and `finish()` * hasn't been called. (Even if the concatenated fragments constitute a * complete JSON text, fragments containing only whitespace can still be * added.) * * Return `false` if no more fragments can be added (because `finish()` was * called or because a previously-added fragment caused a JSON syntax error). */ done(): boolean; }