mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-02 07:43:42 +08:00
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.
94 lines
2.8 KiB
JavaScript
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()));
|