Streams, events & timers

Reference for node:stream (with its web, promises, and consumers sub-modules), node:events, and the timer surface (node:timers and the timer globals). Lists which stream classes, EventEmitter methods, and timer functions work, centered on the readable-drain path, and names the gaps.

This page is the reference for node:stream (with its stream/web, stream/promises, and stream/consumers sub-modules), node:events, and the timer surface (node:timers, node:timers/promises, and the timer globals). These modules are focused or partial: node:stream in particular is built around the common readable-drain path and stops short of the full Node streams state machine. Anything not listed is not present. All modules resolve under both node: and bare specifiers, from require and import. node:readable-stream also resolves (to a compat namespace) for packages that depend on the userland readable-stream module.

node:stream, focused (readable-drain path)

node:stream is focused on the readable interfaces real packages actually exercise, the canonical one being Readable.from() delivering buffered chunks to data listeners and then end, in Node's registration order. The class constructors, pipeline, finished, async iteration, and the consumers all work for in-memory flows. It does not claim full parity on backpressure timing, highWaterMark-driven pause/resume mechanics, error-propagation edge cases, or the long tail of _read/_writev/_final contract details.

ExportSemantics
ReadableReadable.from(iterable/async iterable) delivers buffered chunks to data listeners, then end. Async iteration (for await) works. resume/pause/pipe present.
WritableConstructor with { write(chunk, enc, cb) } works as a pipeline destination.
TransformConstructor with { transform(chunk, enc, cb) } works inside pipeline.
Duplex, PassThroughPresent; PassThrough works (pt.end("z") then consume).
StreamLegacy base class, also the module's default export shape.
pipelineCallback form present; also exercised via the promises form (below).
finishedCallback fires with no error after a drained readable.
addAbortSignal, compose, duplexPair, destroyPresent.
isReadable, isWritable, isDestroyed, isDisturbed, isErroredPresent.
getDefaultHighWaterMark, setDefaultHighWaterMarkPresent.
promisesThe stream/promises namespace (below).
_isUint8Array, _isArrayBufferView, _uint8ArrayToBufferInternal-compat helpers (userland readable-stream ecosystem expects them).

Example:

$ cruft -e 'const {Readable}=require("stream"); let s=""; const r=Readable.from(["a","b","c"]); r.on("data",c=>s+=c); r.on("end",()=>console.log("drained:",s))'
drained: abc

Not implemented / gaps: no claim of full backpressure/highWaterMark parity; Readable.fromWeb/Readable.toWeb bridging is not present in the namespace; object-mode edge semantics and _writev/cork/uncork fidelity are unverified. fetch response bodies expose a real streaming ReadableStream, so the fetch-to-web-stream bridge works.

Retained node:stream host objects cannot cross a CruftScript compartment boundary (an explicit runtime refusal, surfaced rather than failing silently).

node:stream/web, focused

Re-exports the WHATWG streams surface (the same constructors as the globals). These are the real WHATWG implementations.

Constructors
ReadableStreamReadableStreamDefaultReaderReadableStreamBYOBReaderReadableStreamBYOBRequestReadableByteStreamControllerReadableStreamDefaultControllerWritableStreamWritableStreamDefaultWriterWritableStreamDefaultControllerTransformStreamTransformStreamDefaultControllerByteLengthQueuingStrategyCountQueuingStrategyTextEncoderStreamTextDecoderStreamCompressionStreamDecompressionStream

Retained constructor objects from this module cannot cross a CruftScript boundary (explicit refusal).

node:stream/promises, focused

ExportSemantics
pipelineawait pipeline(Readable.from([...]), transform, writable) completes with transformed output delivered.
finishedPromise form of stream.finished.

node:stream/consumers, focused

All consumers present:

Consumers
textjsonbufferarrayBufferbytesblob

Example:

$ cruft -e 'const {text}=require("stream/consumers"); const {Readable}=require("stream"); text(Readable.from(["x","y"])).then(s=>console.log(s))'
xy

node:events, focused

EventEmitter is the module's default export and the workhorse; the static helper surface is present with Node's compatibility statics.

ExportSemantics
EventEmitteron/once/off/emit/removeListener/removeAllListeners, prependListener/prependOnceListener, listeners/rawListeners, listenerCount, eventNames, setMaxListeners/getMaxListeners (default 10). Registration-order delivery and once self-removal.
once(emitter, name)Promise form resolves with the args array.
on(emitter, name)Async-iterator form yields per emit; break cleans up.
listenerCount(emitter, name)Present.
getEventListeners, getMaxListeners, setMaxListeners, addAbortListenerPresent.
captureRejections, captureRejectionSymbol, errorMonitor, defaultMaxListeners, usingDomainsStatics present. captureRejectionSymbol and errorMonitor are real Symbols, matching Node.
EventEmitterAsyncResourcePresent as a constructor.
initInternal-compat export.

Example:

$ cruft -e 'const {EventEmitter,once}=require("events"); const e=new EventEmitter(); setTimeout(()=>e.emit("z","later"),5); once(e,"z").then(([v])=>console.log("awaited:",v))'
awaited: later

Not implemented / gaps: captureRejections behavior (rejection routing from async listeners) is unverified.

Timers (node:timers, node:timers/promises, globals), focused

The timer globals are the primary surface; node:timers re-exports them as a namespace and node:timers/promises provides the promise forms.

Globals
setTimeoutsetIntervalsetImmediateclearTimeoutclearIntervalclearImmediatequeueMicrotask

node:timers

ExportSemantics
setTimeout / clearTimeoutFires after the delay; cleared handles never fire. Handles are objects.
setInterval / clearIntervalPresent and functional.
setImmediate / clearImmediatesetImmediate callbacks fire before same-tick setTimeout(fn, 1) timers, as in Node.
promisesThe timers/promises namespace.

node:timers/promises

ExportSemantics
setTimeout(ms, value)Resolves with value after at least ms.
setImmediate(value)Present and functional.
setInterval(ms, value)Async iterator (for await yields value per tick; break stops it).
schedulerscheduler.wait(ms) works; scheduler.yield present.

Example:

$ cruft -e 'const {setTimeout:st}=require("timers/promises"); const t0=Date.now(); st(20,"val").then(v=>console.log(v, Date.now()-t0>=15))'
val true

Timer-handle ref()/unref() work: an unref'd timer no longer keeps the event loop alive, so the process can exit while it is pending, matching Node. Not implemented / gaps: refresh() does not reliably reset the deadline; AbortSignal support on the promise forms is unverified; there is no timers/promises scheduler.postTask. Timer callbacks run on the runtime's single-threaded event loop; long synchronous work delays them exactly as in Node.