2024-01-01 21:11:32 +00:00
|
|
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
2023-03-18 12:36:00 +00:00
|
|
|
// This module is browser compatible.
|
2022-06-20 00:06:37 +00:00
|
|
|
|
2024-01-05 05:37:42 +00:00
|
|
|
/** Options for {@linkcode JsonStringifyStream}. */
|
2022-06-20 00:06:37 +00:00
|
|
|
export interface StringifyStreamOptions {
|
2024-01-05 05:37:42 +00:00
|
|
|
/**
|
|
|
|
* Prefix to be added after stringify.
|
2022-11-25 11:40:23 +00:00
|
|
|
*
|
|
|
|
* @default {""}
|
|
|
|
*/
|
2022-06-20 00:06:37 +00:00
|
|
|
readonly prefix?: string;
|
2024-01-05 05:37:42 +00:00
|
|
|
/**
|
|
|
|
* Suffix to be added after stringify.
|
2022-11-25 11:40:23 +00:00
|
|
|
*
|
|
|
|
* @default {"\n"}
|
|
|
|
*/
|
2022-06-20 00:06:37 +00:00
|
|
|
readonly suffix?: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert each chunk to JSON string.
|
|
|
|
*
|
2024-01-31 22:19:46 +00:00
|
|
|
* This can be used to stringify {@link https://jsonlines.org/ | JSON lines},
|
|
|
|
* {@link https://ndjson.org/ | NDJSON},
|
2024-05-20 07:14:09 +00:00
|
|
|
* {@link https://www.rfc-editor.org/rfc/rfc7464.html | JSON Text Sequences},
|
2024-01-31 22:19:46 +00:00
|
|
|
* and {@link https://en.wikipedia.org/wiki/JSON_streaming#Concatenated_JSON | Concatenated JSON}.
|
2024-01-05 05:37:42 +00:00
|
|
|
*
|
2024-01-31 22:19:46 +00:00
|
|
|
* You can optionally specify a prefix and suffix for each chunk. The default prefix is `""` and the default suffix is `"\n"`.
|
2022-06-20 00:06:37 +00:00
|
|
|
*
|
2024-06-18 10:10:57 +00:00
|
|
|
* @example Basic usage
|
|
|
|
*
|
2022-06-20 00:06:37 +00:00
|
|
|
* ```ts
|
2024-07-02 01:57:00 +00:00
|
|
|
* import { JsonStringifyStream } from "@std/json/stringify-stream";
|
refactor(assert,async,bytes,cli,collections,crypto,csv,data-structures,datetime,dotenv,encoding,expect,fmt,front-matter,fs,html,http,ini,internal,io,json,jsonc,log,media-types,msgpack,net,path,semver,streams,testing,text,toml,ulid,url,uuid,webgpu,yaml): import from `@std/assert` (#5199)
* refactor: import from `@std/assert`
* update
2024-06-30 08:30:10 +00:00
|
|
|
* import { assertEquals } from "@std/assert";
|
2022-06-20 00:06:37 +00:00
|
|
|
*
|
2024-06-18 10:10:57 +00:00
|
|
|
* const stream = ReadableStream.from([{ foo: "bar" }, { baz: 100 }])
|
|
|
|
* .pipeThrough(new JsonStringifyStream());
|
2022-06-20 00:06:37 +00:00
|
|
|
*
|
2024-06-18 10:10:57 +00:00
|
|
|
* assertEquals(await Array.fromAsync(stream), [
|
|
|
|
* `{"foo":"bar"}\n`,
|
|
|
|
* `{"baz":100}\n`
|
|
|
|
* ]);
|
2022-11-25 11:40:23 +00:00
|
|
|
* ```
|
|
|
|
*
|
2024-06-18 10:10:57 +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
|
2024-07-02 01:57:00 +00:00
|
|
|
* import { JsonStringifyStream } from "@std/json/stringify-stream";
|
refactor(assert,async,bytes,cli,collections,crypto,csv,data-structures,datetime,dotenv,encoding,expect,fmt,front-matter,fs,html,http,ini,internal,io,json,jsonc,log,media-types,msgpack,net,path,semver,streams,testing,text,toml,ulid,url,uuid,webgpu,yaml): import from `@std/assert` (#5199)
* refactor: import from `@std/assert`
* update
2024-06-30 08:30:10 +00:00
|
|
|
* import { assertEquals } from "@std/assert";
|
2022-11-25 11:40:23 +00:00
|
|
|
*
|
2024-06-18 10:10:57 +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
|
|
|
*
|
2024-06-18 10:10:57 +00:00
|
|
|
* assertEquals(await Array.fromAsync(stream), [
|
|
|
|
* `\x1E{"foo":"bar"}\n`,
|
|
|
|
* `\x1E{"baz":100}\n`
|
|
|
|
* ]);
|
2022-06-20 00:06:37 +00:00
|
|
|
* ```
|
2022-11-25 11:40:23 +00:00
|
|
|
*
|
2024-06-18 10:10:57 +00:00
|
|
|
* @example Stringify JSON lines from a server
|
|
|
|
*
|
2024-09-19 23:29:31 +00:00
|
|
|
* ```ts ignore no-assert
|
2024-07-02 01:57:00 +00:00
|
|
|
* 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
|
2023-07-21 03:29:01 +00:00
|
|
|
* Deno.serve(() => {
|
2022-11-25 11:40:23 +00:00
|
|
|
* let intervalId: number | undefined;
|
|
|
|
* const readable = new ReadableStream({
|
|
|
|
* start(controller) {
|
2024-06-18 10:10:57 +00:00
|
|
|
* // 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
|
2024-06-18 10:10:57 +00:00
|
|
|
* .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);
|
|
|
|
* });
|
|
|
|
* ```
|
2022-06-20 00:06:37 +00:00
|
|
|
*/
|
2022-08-30 07:08:43 +00:00
|
|
|
export class JsonStringifyStream extends TransformStream<unknown, string> {
|
2024-06-18 10:10:57 +00:00
|
|
|
/**
|
|
|
|
* Constructs new instance.
|
|
|
|
*
|
2024-07-19 04:12:00 +00:00
|
|
|
* @param options Options for the stream.
|
2024-06-18 10:10:57 +00:00
|
|
|
*/
|
2024-07-19 04:12:00 +00:00
|
|
|
constructor(options?: StringifyStreamOptions) {
|
|
|
|
const { prefix = "", suffix = "\n" } = options ?? {};
|
2022-06-20 00:06:37 +00:00
|
|
|
super(
|
|
|
|
{
|
|
|
|
transform(chunk, controller) {
|
|
|
|
controller.enqueue(`${prefix}${JSON.stringify(chunk)}${suffix}`);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|