feat(stream): make BufferedTransformStream propagate cancel signal immediately

This commit is contained in:
Simon Chan 2025-02-20 11:44:16 +08:00
parent 24b65fd2c1
commit cb21cd2431
No known key found for this signature in database
GPG key ID: A8B69F750B9BCEDD

View file

@ -4,7 +4,10 @@ import { StructEmptyError } from "@yume-chan/struct";
import { BufferedReadableStream } from "./buffered.js"; import { BufferedReadableStream } from "./buffered.js";
import type { PushReadableStreamController } from "./push-readable.js"; import type { PushReadableStreamController } from "./push-readable.js";
import { PushReadableStream } from "./push-readable.js"; import { PushReadableStream } from "./push-readable.js";
import type { ReadableWritablePair } from "./stream.js"; import type {
ReadableWritablePair,
WritableStreamDefaultController,
} from "./stream.js";
import { ReadableStream, WritableStream } from "./stream.js"; import { ReadableStream, WritableStream } from "./stream.js";
// TODO: BufferedTransformStream: find better implementation // TODO: BufferedTransformStream: find better implementation
@ -25,11 +28,13 @@ export class BufferedTransformStream<T>
transform: (stream: BufferedReadableStream) => MaybePromiseLike<T>, transform: (stream: BufferedReadableStream) => MaybePromiseLike<T>,
) { ) {
// Convert incoming chunks to a `BufferedReadableStream` // Convert incoming chunks to a `BufferedReadableStream`
let sourceStreamController!: PushReadableStreamController<Uint8Array>; let bufferedStreamController!: PushReadableStreamController<Uint8Array>;
let writableStreamController!: WritableStreamDefaultController;
const buffered = new BufferedReadableStream( const buffered = new BufferedReadableStream(
new PushReadableStream<Uint8Array>((controller) => { new PushReadableStream<Uint8Array>((controller) => {
sourceStreamController = controller; bufferedStreamController = controller;
}), }),
); );
@ -50,21 +55,26 @@ export class BufferedTransformStream<T>
} }
}, },
cancel: (reason) => { cancel: (reason) => {
// Propagate cancel to the source stream // If a `ReadableStream` is piping into `#writable`,
// So future writes will be rejected // This will cancel the `ReadableStream` immediately.
return buffered.cancel(reason); // If upstream is writing using `#writable`'s writer, this will
// throw errors for any future writes
return writableStreamController.error(reason);
}, },
}); });
this.#writable = new WritableStream({ this.#writable = new WritableStream({
start(controller) {
writableStreamController = controller;
},
async write(chunk) { async write(chunk) {
await sourceStreamController.enqueue(chunk); await bufferedStreamController.enqueue(chunk);
}, },
abort() { abort() {
sourceStreamController.close(); bufferedStreamController.close();
}, },
close() { close() {
sourceStreamController.close(); bufferedStreamController.close();
}, },
}); });
} }