Files
scriptc/tests/corpus/6470-stream-transform-any-data-callback.ts
T
Malte UblandClaude Opus 5.5 447e9bf000 Compile Transform option callbacks and loosely typed child-process listeners
@types/node types the transform/flush completion callback's data as any
and a child stream 'data' chunk as any, and ChildProcess accepts
(code: number, signal: string) exit and close listeners through its
catch-all overload. None of these compiled: the any-typed completion
callback crashed the LLVM emitter, and the child listeners were refused.

The completion callback's data parameter is typed Buffer | string |
undefined while the option callback lowers. Child stream 'data' listeners
with an unknown or any chunk box it as a checked-dynamic value, taking
strings when a setEncoding statement on the same stream precedes the
listener and Buffers when the function never calls setEncoding on it;
other placements are refused. Exit and close listener parameters declared
number or string bind as checked-dynamic values, so the body sees the
null Node passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 04:16:06 -07:00

61 lines
1.8 KiB
TypeScript

// Transform option callbacks whose completion callback declares an
// any-typed `data` (the @types/node TransformCallback spelling): the data
// passed back may be a Buffer, a string, or omitted.
import { PassThrough, Transform } from "node:stream";
import { createGunzip, gzipSync } from "node:zlib";
type Done = (error?: Error | null, data?: any) => void;
interface Progress {
bytes: number;
chunks: number;
}
function counting(progress: Progress): Transform {
return new Transform({
transform(chunk: Buffer, _encoding: string, callback: Done) {
progress.bytes += chunk.length;
progress.chunks++;
callback(null, chunk);
},
});
}
const upper = new Transform({
transform(chunk: Buffer, _encoding: string, callback: Done) {
const text = chunk.toString();
if (text === "skip") {
callback();
return;
}
callback(null, text.toUpperCase());
},
flush(callback: Done) {
callback(null, Buffer.from("!"));
},
});
upper.on("data", (d: Buffer) => console.log("upper", JSON.stringify(d.toString())));
upper.on("end", () => console.log("upper end"));
upper.write("hello");
upper.write("skip");
upper.end("world");
const failing = new Transform({
transform(chunk: Buffer, _encoding: string, callback: Done) {
callback(new Error(`refused ${chunk.length} bytes`));
},
});
failing.on("error", (e: Error) => console.log("error", e.message));
failing.write("abc");
const progress: Progress = { bytes: 0, chunks: 0 };
const counter = counting(progress);
const out = new PassThrough();
counter.pipe(createGunzip()).pipe(out);
const parts: string[] = [];
out.on("data", (d: Buffer) => parts.push(d.toString()));
out.on("end", () => {
console.log("gunzip", JSON.stringify(parts.join("")), progress.bytes > 0, progress.chunks);
});
counter.end(gzipSync(Buffer.from("compressed payload")));