UNPKG

json-stream-es

Version:

A streaming JSON parser/stringifier using web streams.

588 lines (505 loc) 27.2 kB
/** * A transform stream that provides an abort() handler to transform stream abortions. * More info can be found on https://stackoverflow.com/a/78489418/242365. */ export declare class AbortHandlingTransformStream<I, O> extends TransformStream<I, O> { constructor(transformer?: Transformer<I, O> & { abort?: TransformerAbortCallback<O>; }, writableStrategy?: QueuingStrategy<I>, readableStrategy?: QueuingStrategy<O>); } /** * A transform stream where rather than specifying a transformer as a constructor argument, you override its methods to implement the * transformation. */ export declare abstract class AbstractTransformStream<I, O> extends AbortHandlingTransformStream<I, O> { constructor(writableStrategy?: QueuingStrategy<I>, readableStrategy?: QueuingStrategy<O>); protected abstract transform(chunk: I, controller: TransformStreamDefaultController<O>): void | Promise<void>; protected flush(controller: TransformStreamDefaultController<O>): void | Promise<void>; protected abort(reason: any, controller: TransformStreamDefaultController<O>): void | Promise<void>; } declare type AnyIterable<T> = Iterable<T> | AsyncIterable<T> | ReadableStream<T>; declare type AnyState<C extends JsonChunk & { path?: JsonPath; }> = ({ type: StateType.ROOT; value: JsonValue | undefined; path: C["path"]; } | { type: StateType.OBJECT_PROPERTY; object: Record<string, JsonValue>; key: string; value: JsonValue | undefined; parent: State<C>; } | { type: StateType.ARRAY_ITEM; array: Array<JsonValue>; value: JsonValue | undefined; parent: State<C>; }); declare type AnyState_2 = { type: StateType_2.START | StateType_2.END; } | { type: (StateType_2.OBJECT_AFTER_START | StateType_2.OBJECT_AFTER_KEY | StateType_2.OBJECT_AFTER_COLON | StateType_2.OBJECT_AFTER_VALUE | StateType_2.OBJECT_AFTER_COMMA | StateType_2.ARRAY_AFTER_START | StateType_2.ARRAY_AFTER_VALUE | StateType_2.ARRAY_AFTER_COMMA); parentState: State_2<typeof VALUE_START_ALLOWED_MULTI[number]>; } | { type: StateType_2.BOOLEAN_OR_NULL; rawValue: string; parentState: State_2<typeof VALUE_START_ALLOWED_MULTI[number]>; } | { type: StateType_2.WHITESPACE; rawValue: string; parentState: State_2<typeof WHITESPACE_ALLOWED[number]>; } | { type: (StateType_2.NUMBER_MINUS | StateType_2.NUMBER_DIGITS | StateType_2.NUMBER_POINT | StateType_2.NUMBER_DECIMAL_DIGITS | StateType_2.NUMBER_E | StateType_2.NUMBER_E_PLUSMINUS | StateType_2.NUMBER_E_DIGITS); rawValue: string; parentState: State_2<typeof VALUE_START_ALLOWED_MULTI[number]>; } | { type: StateType_2.STRING; value: string; rawValue: string; role: StringRole; parentState: State_2<typeof VALUE_START_ALLOWED_MULTI[number] | typeof KEY_START_ALLOWED[number]>; } | { type: StateType_2.STRING_AFTER_BACKSLASH; rawValue: string; parentState: State_2<StateType_2.STRING>; } | { type: StateType_2.STRING_AFTER_BACKSLASH_U; /** The unicode hex code */ value: string; rawValue: string; parentState: State_2<StateType_2.STRING>; }; export declare function arrayEnd(rawValue?: string): JsonChunk<JsonChunkType.ARRAY_END>; export declare function arrayStart(rawValue?: string): JsonChunk<JsonChunkType.ARRAY_START>; export declare function arrayStartsWith<T>(array: T[], startsWith: T[]): boolean; export declare type ArrayStream<V> = ReadableStream<V> & { [arrayStreamSymbol]: true; }; export declare function arrayStream<T>(obj: AnyIterable<T>): ArrayStream<T>; declare const arrayStreamSymbol: unique symbol; export declare function booleanValue(value: boolean, rawValue?: string): JsonChunk<JsonChunkType.BOOLEAN_VALUE>; export declare function colon(rawValue?: string): JsonChunk<JsonChunkType.COLON>; export declare function comma(rawValue?: string): JsonChunk<JsonChunkType.COMMA>; export declare function concatStreams<T>(...streams: Array<ReadableStream<T> | (() => ReadableStream<T>)>): ReadableStream<T>; declare type Context = { char: string; position: number; }; /** * Converts a stream of JsonChunks into a single JsonValue. The input stream must contain exactly one JSON documents on the root level. */ export declare function deserializeJsonValue(stream: ReadableStream<JsonChunk>): Promise<JsonValue>; export declare function isArrayStream(value: any): value is ArrayStream<any>; export declare function isObjectStream(value: any): value is ObjectStream<any>; export declare function isStringStream(value: any): value is StringStream; /** * Converts a sync/async iterable into an UnderlyingDefaultSource, which can be used as the argument to construct a ReadableStream. */ export declare function iterableToSource<T>(iterable: AsyncIterable<T> | Iterable<T>): UnderlyingDefaultSource<T>; export declare function iterableToStream<T>(iterable: AsyncIterable<T> | Iterable<T>, strategy?: QueuingStrategy<T>): ReadableStream<T>; export declare type JsonChunk<Type extends JsonChunkType = JsonChunkType> = Extract<({ type: JsonChunkType.WHITESPACE; } | { type: JsonChunkType.COMMA; } | { type: JsonChunkType.COLON; } | { type: JsonChunkType.OBJECT_START; } | { type: JsonChunkType.OBJECT_END; } | { type: JsonChunkType.ARRAY_START; } | { type: JsonChunkType.ARRAY_END; } | { type: JsonChunkType.STRING_START; role: StringRole; } | { type: JsonChunkType.STRING_CHUNK; role: StringRole; /** The string value of the string value, without quotes and with backslash escapes resolved. */ value: string; } | { type: JsonChunkType.STRING_END; role: StringRole; } | { type: JsonChunkType.NUMBER_VALUE; value: number; } | { type: JsonChunkType.BOOLEAN_VALUE; value: boolean; } | { type: JsonChunkType.NULL_VALUE; value: null; }) & { /** The raw JSON code for this chunk. The concatenated rawValues of all chunks form a valid JSON value. */ rawValue: string; }, { type: Type; }>; export declare enum JsonChunkType { /** A whitespace that appears between JSON tokens and has no semantic meaning. */ WHITESPACE = "WHITESPACE", /** A comma that separates two array/object items. */ COMMA = "COMMA", /** A colon that separates an object key from its value. */ COLON = "COLON", /** * The start of an object, represented by a curly open bracket. Will be followed by zero or more properties, each one represented * by a string key (one STRING_KEY_START, zero or more STRING_KEY_CHUNKs, one STRING_KEY_END) for the key, a * COLON, a series of chunks for the value and a COMMA (except for the last property); and finally an OBJECT_END. */ OBJECT_START = "OBJECT_START", /** The end of an object, represented by a curly close bracket. */ OBJECT_END = "OBJECT_END", /** * The start of an array, represented by a square open bracket. Will be followed by zero or more chunks representing the values, * each followed by a COMMA (except the last one); and finally an ARRAY_END. */ ARRAY_START = "ARRAY_START", /** The end of an array, represented by a square close bracket. */ ARRAY_END = "ARRAY_END", /** * The start of a string, represented by a double quote. Will be followed by zero or more STRING_CHUNKs and finally * a STRING_END. */ STRING_START = "STRING_START", /** * A section of a string value. Unicode characters are always fully included, so an escape value like \uffff will never span across multiple chunks. */ STRING_CHUNK = "STRING_CHUNK", /** The end of a string value, represented by a double quote. */ STRING_END = "STRING_END", /** A number. May be positive/negative and an integer/float, and the raw value can have an exponent. */ NUMBER_VALUE = "NUMBER_VALUE", /** A boolean, either true or false. */ BOOLEAN_VALUE = "BOOLEAN_VALUE", /** A null value. */ NULL_VALUE = "NULL_VALUE" } export declare type JsonChunkWithPath = JsonChunk & { path: JsonPath; }; /** * Converts a stream of JsonChunks into JsonValues. The input stream may contain multiple JSON documents on the root level, as * produced by JsonPathSelector or by concatenating multiple JsonChunk streams. */ export declare class JsonDeserializer<C extends JsonChunk & { path?: JsonPath; } = JsonChunkWithPath> extends AbstractTransformStream<C, JsonValueAndOptionalPath<C>> { protected state: State<C>; constructor(); protected handleValueEnd(controller: TransformStreamDefaultController<JsonValueAndOptionalPath<C>>): void; protected transform(chunk: C, controller: TransformStreamDefaultController<JsonValueAndOptionalPath<C>>): void; } /** * Parses a JSON string stream into a stream of JsonChunks. * Unless multi is true, the JSON string must contain only one JSON value (object/array/string/number/boolean/null) * on the root level, otherwise the stream will fail with an error. */ export declare class JsonParser extends AbstractTransformStream<string, JsonChunk> { protected options: JsonParserOptions; protected state: State_2; protected lengthBeforeCurrentChunk: number; constructor(options?: JsonParserOptions); /** * Checks whether a token that doesn't have an explicit end character (that is: numbers and whitespaces) has ended, and if * so, update the state and emit the appropriate chunks. * @param char The next character on the stream. Is used to check whether the current token ends (for example, a number is ended * by a non-number character). If undefined, the stream is assumed to have ended, so the current token must always end. */ protected checkValueEnd(controller: TransformStreamDefaultController<JsonChunk>, char: string | undefined): void; /** * Handle a single character piped into the stream. */ protected handleChar(controller: TransformStreamDefaultController<JsonChunk>, context: Context): void; /** * Called at the end of the transformation of a chunk. Should flush partial values where applicable, * in particular and incomplete strings or whitespaces can be emitted. */ protected handleChunkEnd(controller: TransformStreamDefaultController<JsonChunk>): void; /** * Transforms an incoming chunk. */ protected transform(chunk: string, controller: TransformStreamDefaultController<JsonChunk>): void; /** * Called when the end of the incoming stream is reached. Checks that a complete value has been emitted. */ protected flush(controller: TransformStreamDefaultController<JsonChunk>): void; } export declare type JsonParserOptions = { /** If true, the stream is allowed to contain multiple JSON values on the root level */ multi?: boolean; }; /** * An array that describes the chain of object and array keys that the current value is located under. * String values represent object keys, while numbers represent array indexes (starting at 0). * The root value’s path is an empty array. * * For objects, any chunks between the colon and the comma (or end of object) will have the property * key in the path, while other chunks (such as the object start/end and the key string start/chunk/end) * will have the path of the object itself. * For arrays, any chunks in between the array start and end except the comma will have the array index * in the path, while other chunks (such as the array start/end and the comma) will have the path of * the array itself. */ export declare type JsonPath = Array<string | number>; /** * Adds a "path" property to all JsonChunks passed through it that indicates the path of object property keys * and array item indexes where the chunk is located. */ export declare class JsonPathDetector extends AbstractTransformStream<JsonChunk, JsonChunkWithPath> { protected stack: Array<{ type: "object"; /** pending: still receiving key STRING_CHUNKs; next: next chunk will transition state to active; active: path applies to all current chunks */ state: "pending" | "next" | "active"; key: string; } | { type: "array"; /** next: next chunk will transition state to active; active: path applies to current chunks */ state: "next" | "active"; key: number; }>; protected path: Array<string | number>; constructor(); protected transform(chunk: JsonChunk, controller: TransformStreamDefaultController<JsonChunkWithPath>): void; } /** * Takes a JsonChunkWithPath stream as emitted by JsonPathDetector and forwards only those chunks that match the specified selector. */ export declare class JsonPathSelector extends AbstractTransformStream<JsonChunkWithPath, JsonChunkWithPath> { protected selector: JsonPathSelectorExpression; protected currentPathPrefix: Array<string | number> | undefined; constructor(selector: JsonPathSelectorExpression); protected transform(chunk: JsonChunkWithPath, controller: TransformStreamDefaultController<JsonChunkWithPath>): void; protected flush(controller: TransformStreamDefaultController<JsonChunkWithPath>): void; } /** * A selector that can be set for a PathSelector stream. * If this is an array, the path has to match the items in the array. Undefined items in the array match any key in the path. Arrays * in the array match any of their values. * If this is a function, it is called with the path and should return true if the path matches the selector. */ export declare type JsonPathSelectorExpression = (Array<Array<string | number> | string | number | undefined> | ((path: JsonPath) => boolean)); /** * Splits up the incoming ReadableStream<JsonChunkWithPath> as emitted by JsonPathSelector and emits a nested * ReadableStream<JsonChunkWithPath> for each JSON document in the stream. Each emitted nested stream gets * a "path" property that contains the path of the document as selected by JsonPathSelector. The individual * JSON chunks of the nested stream have the path prefix of their document removed, so that the nested * stream can be piped through the other transformers (such as JsonPathSelector or JsonDeserializer) as if * it contained an independent JSON document. */ export declare class JsonPathStreamSplitter extends StreamSplitter<JsonChunkWithPath, P> { constructor(); } /** * Converts any JSON-stringifiable JavaScript values into a stream of JsonChunks. */ export declare class JsonSerializer extends AbstractTransformStream<SerializableJsonValue, JsonChunk> { protected space?: string | number | undefined; protected options?: JsonSerializerOptions | undefined; protected first: boolean; constructor(space?: string | number | undefined, options?: JsonSerializerOptions | undefined); transform(value: SerializableJsonValue, controller: TransformStreamDefaultController<JsonChunk>): Promise<void>; protected flush(controller: TransformStreamDefaultController<JsonChunk>): void | Promise<void>; } export declare type JsonSerializerOptions = { /** White space characters to insert before the first emitted root value. Not emitted if no values are emitted. */ beforeFirst?: string; /** White space characters to insert before each but the first emitted root value. Defaults to a newline (\n). */ delimiter?: string; /** White space characters to insert after the last emitted root value. Not emitted if no values are emitted. */ afterLast?: string; }; export declare type JsonStreamWithPath = ReadableStream<JsonChunkWithPath> & P; /** * Converts a stream of JsonChunks into a JSON string stream. */ export declare class JsonStringifier extends AbstractTransformStream<JsonChunk | { rawValue: string; }, string> { constructor(); protected transform(chunk: JsonChunk | { rawValue: string; }, controller: TransformStreamDefaultController<string>): void; protected flush(controller: TransformStreamDefaultController<string>): void; } /** A JavaScript value that can be stringified to JSON. */ export declare type JsonValue = { [key: string]: JsonValue; } | Array<JsonValue> | string | number | boolean | null; export declare type JsonValueAndOptionalPath<C extends JsonChunk & { path?: JsonPath; }> = { value: JsonValue; path: C["path"]; }; export declare type JsonValueAndPath = JsonValueAndOptionalPath<JsonChunkWithPath>; /** States whree the start of an object key string is allowed. */ declare const KEY_START_ALLOWED: readonly [StateType_2.OBJECT_AFTER_START, StateType_2.OBJECT_AFTER_COMMA]; export declare function matchesJsonPathSelector(path: JsonPath, selector: JsonPathSelectorExpression): boolean; export declare function nullValue(rawValue?: string): JsonChunk<JsonChunkType.NULL_VALUE>; export declare function numberValue(value: number, rawValue?: string): JsonChunk<JsonChunkType.NUMBER_VALUE>; export declare function objectEnd(rawValue?: string): JsonChunk<JsonChunkType.OBJECT_END>; export declare function objectStart(rawValue?: string): JsonChunk<JsonChunkType.OBJECT_START>; export declare type ObjectStream<V> = ReadableStream<[key: string | StringStream, value: V]> & { [objectStreamSymbol]: true; }; export declare function objectStream<T>(obj: AnyIterable<[key: string | StringStream, value: T]>): ObjectStream<T>; declare const objectStreamSymbol: unique symbol; declare type P = { path: JsonPath; }; export declare function parseJsonStream(selector: JsonPathSelectorExpression | undefined, options?: JsonParserOptions): TransformStream<string, JsonValue>; export declare function parseJsonStreamWithPaths(selector: JsonPathSelectorExpression | undefined, options?: JsonParserOptions): TransformStream<string, JsonValueAndPath>; export declare function parseNestedJsonStream(selector: JsonPathSelectorExpression, options?: JsonParserOptions): TransformStream<string, ReadableStream<JsonValue> & { path: JsonPath; }>; export declare function parseNestedJsonStreamWithPaths(selector: JsonPathSelectorExpression, options?: JsonParserOptions): TransformStream<string, ReadableStream<JsonValueAndPath> & { path: JsonPath; }>; /** * A TransformStream that is set up by providing a ReadableStream mapper rather than transforming individual chunks using * start(), transform() and flush(). * This allows access to ReadableStream methods such as pipeThrough(), which makes it easy to reuse other TransformStreams * in the implementation. * @param transformReadable Retrieves one parameter with a ReadableStream that emits the input data of the TransformStream. * Should return a ReadableStream whose output will become the output data of the TransformStream. */ export declare class PipeableTransformStream<I, O> extends TransformStream<I, O> { constructor(transformReadable: (readable: ReadableStream<I>) => ReadableStream<O>, writableStrategy?: QueuingStrategy<I>, readableStrategy?: QueuingStrategy<O>); } export declare class PrematureEndError extends Error { constructor(); } export declare type SerializableJsonValue = SyncOrAsync<{ [key: string | number]: SerializableJsonValue; } | (ReadableStream<[key: string | StringStream, value: SerializableJsonValue]> & { [objectStreamSymbol]: true; }) | Array<SerializableJsonValue> | (ReadableStream<SerializableJsonValue> & { [arrayStreamSymbol]: true; }) | string | StringStream | number | boolean | null | undefined>; /** * Converts any single JSON-stringifiable JavaScript value into a stream of JsonChunks. */ export declare function serializeJsonValue(value: SerializableJsonValue, space?: string | number): ReadableStream<JsonChunk>; declare type State<C extends JsonChunk & { path?: JsonPath; }, Type extends StateType = StateType> = Extract<AnyState<C>, { type: Type; }>; declare type State_2<T extends StateType_2 = StateType_2> = AnyState_2 & { type: T; }; declare enum StateType { ROOT = "ROOT", OBJECT_PROPERTY = "OBJECT_PROPERTY", ARRAY_ITEM = "ARRAY_ITEM" } declare enum StateType_2 { START = "start", OBJECT_AFTER_START = "object_after_start", OBJECT_AFTER_KEY = "object_after_key", OBJECT_AFTER_COLON = "object_after_colon", OBJECT_AFTER_VALUE = "object_after_value", OBJECT_AFTER_COMMA = "object_after_comma", ARRAY_AFTER_START = "array_after_start", ARRAY_AFTER_VALUE = "array_after_value", ARRAY_AFTER_COMMA = "array_after_comma", BOOLEAN_OR_NULL = "boolean_or_null", NUMBER_MINUS = "number_minus", NUMBER_DIGITS = "number_digits", NUMBER_POINT = "number_point", NUMBER_DECIMAL_DIGITS = "number_decimal_digits", NUMBER_E = "number_e", NUMBER_E_PLUSMINUS = "number_e_plusminus", NUMBER_E_DIGITS = "number_e_digits", WHITESPACE = "whitespace", STRING = "string", STRING_AFTER_BACKSLASH = "string_after_backslash", STRING_AFTER_BACKSLASH_U = "string_after_backslash_u", END = "end" } /** * Transforms a ReadableStream of chunks into a ReadableStream of ReadableStreams of chunks */ export declare class StreamSplitter<I, P extends Record<any, any> = {}> extends TransformStream<I, ReadableStream<I> & P> { protected options: { /** * When a new nested readable stream is created, the properties returned by this function are added to the stream * object. The provided chunk parameter contains the first chunk that will be emitted on the nested stream. */ getNestedStreamProperties(chunk: I): P; /** * Is called for each chunk with the currently active nested stream (containing the properties created by * getNestedStreamProperties). If this returns false, the current nested stream is closed and a new nested * stream is started, with the current chunk being the first one emitted. * If not defined, getNestedStreamProperties() is called for each chunk and a new nested stream is started * whenever its result changes (compared by a shallow equality check, checking for strict equality of all * the object properties). */ belongsToNestedStream?: (chunk: I, stream: ReadableStream<I> & P) => boolean; }; protected lastChunkIdx: number | undefined; protected nestedStreams: Record<number, ReadableStream<I> & P>; protected currentNestedStream: (ReadableStream<I> & P) | undefined; protected nestedWriters: Record<number, WritableStreamDefaultWriter<I>>; protected currentWriter: WritableStreamDefaultWriter<I> | undefined; constructor(options: { /** * When a new nested readable stream is created, the properties returned by this function are added to the stream * object. The provided chunk parameter contains the first chunk that will be emitted on the nested stream. */ getNestedStreamProperties(chunk: I): P; /** * Is called for each chunk with the currently active nested stream (containing the properties created by * getNestedStreamProperties). If this returns false, the current nested stream is closed and a new nested * stream is started, with the current chunk being the first one emitted. * If not defined, getNestedStreamProperties() is called for each chunk and a new nested stream is started * whenever its result changes (compared by a shallow equality check, checking for strict equality of all * the object properties). */ belongsToNestedStream?: (chunk: I, stream: ReadableStream<I> & P) => boolean; }); protected transformMain([chunkIdx, chunk]: [number, I], controller: TransformStreamDefaultController<ReadableStream<I> & P>): void; protected handleNestedChunk([chunkIdx, chunk]: [number, I]): Promise<void>; protected handleNestedClose(): Promise<void>; protected handleNestedAbort(reason: any): Promise<void>; protected handleChunk([chunkIdx, chunk]: [number, I]): void; protected belongsToNestedStream(chunk: I, stream: ReadableStream<I> & P): boolean; } export declare function streamToArray<T>(stream: ReadableStream<T>): Promise<T[]>; /** * Converts a ReadableStream into an AsyncIterable so the stream can be consumed using "for await". * In the latest Streams API, ReadableStream is an AsyncIterable, but not all browsers support this yet. */ export declare function streamToIterable<T>(stream: ReadableStream<T>): AsyncGenerator<T, void, undefined>; export declare function streamToString(stream: ReadableStream<string>): Promise<string>; export declare function stringChunk(value: string, role?: StringRole, rawValue?: string): JsonChunk<JsonChunkType.STRING_CHUNK>; export declare function stringEnd(role?: StringRole, rawValue?: string): JsonChunk<JsonChunkType.STRING_END>; export declare function stringifyJsonStream(value: SerializableJsonValue, space?: string | number): ReadableStream<string>; export declare function stringifyMultiJsonStream(space?: string | number, options?: JsonSerializerOptions): TransformStream<SerializableJsonValue, string>; export declare enum StringRole { /** A string used as a property key inside an object. */ KEY = "KEY", /** A string used as a value. */ VALUE = "VALUE" } export declare function stringStart(role?: StringRole, rawValue?: string): JsonChunk<JsonChunkType.STRING_START>; export declare type StringStream = ReadableStream<string> & { [stringStreamSymbol]: true; }; export declare function stringStream(stream: AnyIterable<string>): StringStream; declare const stringStreamSymbol: unique symbol; export declare function stringToStream(string: string): ReadableStream<string>; declare type SyncOrAsync<T> = T | Promise<T> | (() => T | Promise<T>); export declare type TransformerAbortCallback<O> = (reason: any, controller: TransformStreamDefaultController<O>) => void | PromiseLike<void>; export declare class UnexpectedCharError extends Error { constructor(context: Context); } declare const VALUE_START_ALLOWED_MULTI: readonly [StateType_2.START, StateType_2.OBJECT_AFTER_COLON, StateType_2.ARRAY_AFTER_START, StateType_2.ARRAY_AFTER_COMMA, StateType_2.END]; export declare function whitespace(rawValue: string): JsonChunk<JsonChunkType.WHITESPACE>; /** States where a whilespace character is allowed. */ declare const WHITESPACE_ALLOWED: readonly [StateType_2.START, StateType_2.OBJECT_AFTER_START, StateType_2.OBJECT_AFTER_KEY, StateType_2.OBJECT_AFTER_COLON, StateType_2.OBJECT_AFTER_VALUE, StateType_2.OBJECT_AFTER_COMMA, StateType_2.ARRAY_AFTER_START, StateType_2.ARRAY_AFTER_VALUE, StateType_2.ARRAY_AFTER_COMMA, StateType_2.END]; export { }