Files
scriptc/tests/corpus/7242-objectmode-dynamic.cjs
T
Chris Tate 67247dd26b Support objectMode streams with any value as a chunk
Readable, Writable, Duplex, Transform and PassThrough take the
objectMode option, and a Duplex, Transform or PassThrough the
readableObjectMode and writableObjectMode options, as true or false
literals in an options object literal or in an options value. An
object-mode side holds any value that converts to a checked-dynamic
value: push, unshift, write, end, pipe, pipeline, for await, 'data'
listeners, Readable.from and the transform callback carry values in
TypeScript and JavaScript, and constructor-function streams and options
held in variables do the same. The object-mode high-water mark counts
values and defaults to 16. A readable buffer that mixes strings, Buffers
and other values holds them all, and push(undefined) and other values on
a byte-mode stream report ERR_INVALID_ARG_TYPE through 'error', as in
Node.

In typed code a write or transform callback, or a _write or _transform
method, may declare its chunk with any such type, which converts when
the callback runs. Writes pass the encoding argument to _write and
_transform as Node does: undefined when absent, null when a callback
took its place, otherwise the value unchanged. A parameter declared
string | null | undefined receives that value and one declared string
receives "" for undefined and null. write(chunk, encoding) on a
byte-mode stream encodes a string chunk with any known encoding and
throws ERR_UNKNOWN_ENCODING for an unknown one. Readable.from takes
typed Maps and Sets of any such value, and arrays that mix strings and
Buffers.

for await over a readable-only stream that will emit 'close' now ends
after 'close', as Node's eos does. Buffered writes that complete inside
clearBuffer run their callbacks in order, and 'finish' waits for a
deferred afterWrite that carries a callback, as Node's pendingcb does,
unless a _final callback schedules it.

The 'data' listener adapters take three slots and write and transform
callbacks read their chunk and encoding through runtime accessors, so
RUNTIME_ABI_VERSION is now 26.
2026-10-22 04:21:55 -05:00

94 lines
2.8 KiB
JavaScript

// Object-mode streams through dynamic values: options objects held in
// variables, constructor functions with util.inherits (Transform.call
// and Writable.call with objectMode), streams passed around as plain
// values, and Readable.from over values of mixed types.
const stream = require("stream");
const util = require("util");
const show = (v) => (v !== null && typeof v === "object" ? JSON.stringify(v) : `${typeof v}:${String(v)}`);
function Tagger(tag) {
stream.Transform.call(this, { objectMode: true });
this.tag = tag;
}
util.inherits(Tagger, stream.Transform);
Tagger.prototype._transform = function (chunk, enc, cb) {
console.log("tagger enc:", enc);
if (chunk === 3) return cb();
cb(null, { tag: this.tag, value: chunk });
};
Tagger.prototype._flush = function (cb) {
this.push({ tag: this.tag, value: "flushed" });
cb();
};
function Collector() {
stream.Writable.call(this, { objectMode: true, highWaterMark: 2 });
this.items = [];
}
util.inherits(Collector, stream.Writable);
Collector.prototype._write = function (chunk, enc, cb) {
this.items.push(show(chunk));
cb();
};
function viaConstructors(next) {
const t = new Tagger("x");
const c = new Collector();
console.log("modes:", t.readableObjectMode, t.writableObjectMode, c.writableObjectMode, c.writableHighWaterMark);
t.pipe(c);
c.on("finish", () => {
console.log("collected:", c.items.join(" "));
next();
});
t.write(1);
t.write("two", () => console.log("write cb"));
t.write(3);
t.end({ four: 4 });
}
function viaOptionObjects(next) {
const readableOpts = { objectMode: true, read() {} };
const r = new stream.Readable(readableOpts);
const transformOpts = {
objectMode: true,
transform(chunk, enc, cb) {
cb(null, Array.isArray(chunk) ? chunk.length : chunk);
},
};
const t = new stream.Transform(transformOpts);
const seen = [];
const writableOpts = {
objectMode: true,
write(chunk, enc, cb) {
seen.push(show(chunk) + "/" + enc);
cb();
},
};
const w = new stream.Writable(writableOpts);
stream.pipeline(r, t, w, (err) => {
console.log("option objects:", err === undefined ? "ok" : err, seen.join(" "));
next();
});
r.push([1, 2, 3]);
r.push(false);
r.push({ k: "v" });
r.push(null);
}
async function viaFrom() {
const values = [0, "s", undefined, [1], { a: { b: 2 } }, 1.5];
const out = [];
for await (const v of stream.Readable.from(values)) out.push(show(v));
console.log("from:", out.join(" "));
const pt = new stream.PassThrough({ objectMode: true });
const passAlong = (s, v) => s.write(v);
passAlong(pt, { through: true });
passAlong(pt, 7);
pt.end();
pt.on("data", (v) => console.log("passed:", show(v)));
await new Promise((resolve) => pt.on("end", resolve));
}
viaConstructors(() => viaOptionObjects(() => viaFrom()));