UNPKG

@xtao-org/jsonhilo

Version:

Pure JavaScript minimal lossless JSON parse event streaming, akin to SAX. Fast, modular, and dependency-free.

256 lines (231 loc) 9.02 kB
/** * `JsonHigh` is called with an object which contains named event handlers that are invoked during parsing. * * The object may also contain [options](#options) to change the default behavior, e.g. [turn off number parsing](#parseNumbers) or turn on [buffering of strings piece-by-piece](#maxStringBufferLength). */ export declare const JsonHigh: JsonHigh /** * This is the high-level interface over {@link JsonLow} optimized for convenience. It provides similar functionality and granularity to other streaming parsers, such as [Clarinet](https://github.com/dscape/clarinet) or [creationix/jsonparse](https://github.com/creationix/jsonparse). */ export type JsonHigh = <Feedback, End>( next: JsonHighHandlers<Feedback, End>, ) => JsonHighStream<End> /** * ### Return value * * {@link JsonHigh} returns a stream object with the following methods: */ export type JsonHighStream<End> = { /** * Accepts a JSON chunk to parse. * * Returns the stream object for chaining. * */ chunk(chunk: string): JsonHighStream<End>, /** * Call it to signal to the stream that the current JSON document is finished. * * If there is no error, it will then call the corresponding `end()` event handler, and return whatever that returned. */ end(): End, /** * Reports current depth (level of nesting). * * **NOTE**: an `open*` or `close*` handler is always called *after* the depth is updated, meaning: * * * in `openObject`/`openArray` handlers the lowest depth reported will be 1 -- we entered a top-level object/array and are now at depth 1 * * * in `closeObject`/`closeArray` handlers the lowest depth reported will be 0 -- we exited a top-level object/array and are now at depth 0 (top-level) */ depth(): number, // todo: type for state() } /** * ### Options */ export type JsonHighOptions = { /** * See also: the similar {@link JsonHighOptions.bufferOnChunk} option. These options can be combined. Differences between them are marked with a strong font. * * The maximum length of the key or string buffer, in code points. * * If **set to a value in range `[1;Infinity)`**, the `key` event handler won't be called and, for strings, the `value` event handler won't be called. * * Instead, the `bufferKey` and `bufferString` handlers will be called **as soon as the given number of code points has been collected** or when the key/string is finished. * * In the latter case **the number of code points may be smaller than `maxStringBufferLength`** and a `closeKey`/`closeString` handler will be called after the last `buffer*` event to signal the finish. * * This is useful **when dealing with long strings** where it's desirable to stream them piece-by-piece, e.g. when working with LLMs (Large Language Models). * * See [Add support for incomplete key and value strings #10](https://github.com/xtao-org/jsonhilo/issues/10) for more information. * * @default {Infinity} */ maxStringBufferLength?: number, /** * See also: the similar {@link JsonHighOptions.maxStringBufferLength} option. These options can be combined. Differences between them are marked with a strong font. * * If **set to `true`**, the `key` event handler won't be called and, for strings, the `value` event handler won't be called. * * Instead, the `bufferKey` and `bufferString` handlers will be called **as soon as the current chunk (passed in via the `.chunk()` method of the stream) has been processed** or when the key/string is finished. * * In the latter case a `closeKey`/`closeString` handler will be called after the last `buffer*` event to signal the finish. * * This is useful **when it's desirable to stream strings or keys piece-by-piece, in sync with the chunks being received, without needing to specify a fixed buffer length;** e.g. when working with LLMs (Large Language Models). * * See [Add support for incomplete key and value strings #10](https://github.com/xtao-org/jsonhilo/issues/10) for more information. * * @default {false} */ bufferOnChunk?: boolean, /** * Specifies the maximum length of a number value (in characters). * * @default {8192} */ maxNumberLength?: number, /** * Controls whether numbers should be parsed (converted to JavaScript `number` type) which is the case by default. * * If set to `false`, the `value` event handler won't be called for numbers. Instead, the `bufferNumber` handler will be called with the number as a string. * * This is useful when dealing with big numbers which would lose precision when converted to the `number` type. * * @default {true} */ parseNumbers?: boolean, } /** ### Basic events * * The basic usage of `JsonHigh` involves 4 event handlers without arguments which indicate start and end of structures: * * {@link JsonHighEvents.openArray} * * {@link JsonHighEvents.closeArray} * * {@link JsonHighEvents.openObject} * * {@link JsonHighEvents.closeObject} * * And 2 event handlers with one argument which capture primitives: * * * {@link JsonHighEvents.key} * * {@link JsonHighEvents.value} * * Finally, there is the argumentless {@link JsonHighEvents.end} event handler. * * ### Extra events * * These handlers take no arguments: * * * {@link JsonHighEvents.openKey} * * {@link JsonHighEvents.openString} * * {@link JsonHighEvents.openNumber} * * ### Conditional events * * These handlers take no arguments: * * * {@link JsonHighEvents.closeKey} * * {@link JsonHighEvents.closeString} * * These handlers receive the buffer that should be consumed: * * * {@link JsonHighEvents.bufferKey} * * {@link JsonHighEvents.bufferString} * * {@link JsonHighEvents.bufferNumber} */ export type JsonHighEvents<Feedback, End> = { /** * An array started (`[`). */ openArray?: JsonHighHandler<Feedback>, /** * An array ended (`]`). */ closeArray?: JsonHighHandler<Feedback>, /** * An object started (`{`). */ openObject?: JsonHighHandler<Feedback>, /** * An object ended (`}`). */ closeObject?: JsonHighHandler<Feedback>, /** * An object's key ended. * * The argument of the handler contains the key as a JavaScript string. * * `[conditional]` Called only in default (non-buffering) mode. * * This event can be suppressed by setting {@link JsonHighOptions.maxStringBufferLength}. */ key?: (key: string) => Feedback, /** * A primitive JSON value ended. * * The argument of the event contains the corresponding JavaScript value: `true`, `false`, `null`, a number, or a string. * * This event can be suppressed for strings if {@link JsonHighOptions.maxStringBufferLength} is set and for numbers if {@link JsonHighOptions.parseNumbers} is set to `false`. * */ value?: (value: string | number | null | boolean) => Feedback, /** * Called by the `end` method of the stream to confirm that the parsed JSON document is complete and valid. */ end?: () => End, /** * A key started (`"`, in key position). */ openKey?: JsonHighHandler<Feedback>, /** * A string value started (`"`). */ openString?: JsonHighHandler<Feedback>, /** * A number value started. */ openNumber?: JsonHighHandler<Feedback>, /** * A key ended (`"`). * * `[conditional]` Called instead of the `key` event **when `maxStringBufferLength` is set**. */ closeKey?: JsonHighHandler<Feedback>, /** * A string value ended (`"`). * * `[conditional]` Called instead of the `value` event **when `maxStringBufferLength` is set**. * * @tags conditional */ closeString?: JsonHighHandler<Feedback>, /** * Key buffer is ready for consumption. * * `[conditional]` Called instead of the `key` event **when `maxStringBufferLength` is set**. * * The `buffer` then contains `maxStringBufferLength` code points or possibly less if we reached the end of a key. */ bufferKey?: (buffer: string) => Feedback, /** * String buffer is ready for consumption. * * `[conditional]` For string values, called instead of the `value` event **when `maxStringBufferLength` is set**. * * The buffer then contains `maxStringBufferLength` code points or possibly less if we reached the end of a string value. */ bufferString?: (buffer: string) => Feedback, /** * Number buffer is ready for consumption. * * `[conditional]` For number values, called instead of the `value` event **when `parseNumbers` is set**. * * The buffer then contains the unparsed number (represented as a string). */ bufferNumber?: (buffer: string) => Feedback, } /** * TODO: this should be called something like JsonHighParameters or JsonHighArguments or sth. */ export type JsonHighHandlers<Feedback, End> = JsonHighOptions & JsonHighEvents<Feedback, End> /** TODO: doc */ export type JsonHighHandler<Feedback> = () => Feedback