async-streamify
Version:
Stream and serialize nested promises and async iterables over HTTP, workers, etc
61 lines (60 loc) • 2.06 kB
JavaScript
import AsyncObjectSerializer from "./asyncObjectSerializer.js";
import transformIterable from "../util/transformIterable.js";
/**
* Extends the standard Response class to support streaming serialized objects
* using AsyncObjectSerializer. The response is formatted as newline-delimited JSON (NDJSON).
*
* @example
* ```typescript
* // In a server handler
* async function handler(req: Request) {
* const data = {
* status: "processing",
* result: Promise.resolve({ completed: true }),
* updates: (async function*() {
* yield "step 1";
* yield "step 2";
* })()
* };
*
* return new AsyncResponse(data, {
* headers: {
* "cache-control": "no-cache"
* }
* });
* }
* ```
*/
export class AsyncResponse extends Response {
/**
* Creates a new AsyncResponse instance
*
* @param body - The object to serialize and stream, or null for an empty response
* @param init - Standard ResponseInit options
*/
constructor(body, init = {}, opts = {}) {
if (body === null) {
super(null, init);
return;
}
if (!opts.transformers) {
opts.transformers = [JSON.stringify];
}
// This final transformer add newlines (for NDJSON) and encodes to bytes
const encoder = new TextEncoder();
opts.transformers.push((value) => encoder.encode(value + "\n"));
const transform = (chunk) => opts.transformers.reduce((acc, cur) => acc = cur(acc), chunk);
const serializer = new AsyncObjectSerializer(body);
const stream = ReadableStream.from(transformIterable(serializer, transform));
// Initialize the response with the stream and NDJSON content type
super(stream, {
...init,
headers: {
...init.headers,
"content-type": "application/x-ndjson",
// consider: "cache-control": "no-cache", // Recommended for streaming responses
},
});
}
}
export default AsyncResponse;