std/json/stringify_stream.ts

108 lines
3.1 KiB
TypeScript
Raw Normal View History

// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
/** Options for {@linkcode JsonStringifyStream}. */
export interface StringifyStreamOptions {
/**
* Prefix to be added after stringify.
2022-11-25 11:40:23 +00:00
*
* @default {""}
*/
readonly prefix?: string;
/**
* Suffix to be added after stringify.
2022-11-25 11:40:23 +00:00
*
* @default {"\n"}
*/
readonly suffix?: string;
}
/**
* Convert each chunk to JSON string.
*
* This can be used to stringify {@link https://jsonlines.org/ | JSON lines},
* {@link https://ndjson.org/ | NDJSON},
* {@link https://www.rfc-editor.org/rfc/rfc7464.html | JSON Text Sequences},
* and {@link https://en.wikipedia.org/wiki/JSON_streaming#Concatenated_JSON | Concatenated JSON}.
*
* You can optionally specify a prefix and suffix for each chunk. The default prefix is `""` and the default suffix is `"\n"`.
*
* @example Basic usage
*
* ```ts
* import { JsonStringifyStream } from "@std/json/stringify-stream";
* import { assertEquals } from "@std/assert";
*
* const stream = ReadableStream.from([{ foo: "bar" }, { baz: 100 }])
* .pipeThrough(new JsonStringifyStream());
*
* assertEquals(await Array.fromAsync(stream), [
* `{"foo":"bar"}\n`,
* `{"baz":100}\n`
* ]);
2022-11-25 11:40:23 +00:00
* ```
*
* @example Stringify stream of JSON text sequences
*
* Set `options.prefix` to `\x1E` to stringify
* {@linkcode https://www.rfc-editor.org/rfc/rfc7464.html | JSON Text Sequences}.
*
2022-11-25 11:40:23 +00:00
* ```ts
* import { JsonStringifyStream } from "@std/json/stringify-stream";
* import { assertEquals } from "@std/assert";
2022-11-25 11:40:23 +00:00
*
* const stream = ReadableStream.from([{ foo: "bar" }, { baz: 100 }])
* .pipeThrough(new JsonStringifyStream({ prefix: "\x1E", suffix: "\n" }));
2022-11-25 11:40:23 +00:00
*
* assertEquals(await Array.fromAsync(stream), [
* `\x1E{"foo":"bar"}\n`,
* `\x1E{"baz":100}\n`
* ]);
* ```
2022-11-25 11:40:23 +00:00
*
* @example Stringify JSON lines from a server
*
* ```ts ignore no-assert
* import { JsonStringifyStream } from "@std/json/stringify-stream";
2022-11-25 11:40:23 +00:00
*
* // A server that streams one line of JSON every second
* Deno.serve(() => {
2022-11-25 11:40:23 +00:00
* let intervalId: number | undefined;
* const readable = new ReadableStream({
* start(controller) {
* // Enqueue data once per second
2022-11-25 11:40:23 +00:00
* intervalId = setInterval(() => {
* controller.enqueue({ now: new Date() });
* }, 1000);
* },
* cancel() {
* clearInterval(intervalId);
* },
* });
*
* const body = readable
* .pipeThrough(new JsonStringifyStream()) // Convert data to JSON lines
* .pipeThrough(new TextEncoderStream()); // Convert a string to a Uint8Array
2022-11-25 11:40:23 +00:00
*
* return new Response(body);
* });
* ```
*/
export class JsonStringifyStream extends TransformStream<unknown, string> {
/**
* Constructs new instance.
*
* @param options Options for the stream.
*/
constructor(options?: StringifyStreamOptions) {
const { prefix = "", suffix = "\n" } = options ?? {};
super(
{
transform(chunk, controller) {
controller.enqueue(`${prefix}${JSON.stringify(chunk)}${suffix}`);
},
},
);
}
}