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.
| Export | Semantics |
|---|---|
Readable | Readable.from(iterable/async iterable) delivers buffered chunks to data listeners, then end. Async iteration (for await) works. resume/pause/pipe present. |
Writable | Constructor with { write(chunk, enc, cb) } works as a pipeline destination. |
Transform | Constructor with { transform(chunk, enc, cb) } works inside pipeline. |
Duplex, PassThrough | Present; PassThrough works (pt.end("z") then consume). |
Stream | Legacy base class, also the module's default export shape. |
pipeline | Callback form present; also exercised via the promises form (below). |
finished | Callback fires with no error after a drained readable. |
addAbortSignal, compose, duplexPair, destroy | Present. |
isReadable, isWritable, isDestroyed, isDisturbed, isErrored | Present. |
getDefaultHighWaterMark, setDefaultHighWaterMark | Present. |
promises | The stream/promises namespace (below). |
_isUint8Array, _isArrayBufferView, _uint8ArrayToBuffer | Internal-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.
Retained constructor objects from this module cannot cross a CruftScript boundary (explicit refusal).
node:stream/promises, focused
| Export | Semantics |
|---|---|
pipeline | await pipeline(Readable.from([...]), transform, writable) completes with transformed output delivered. |
finished | Promise form of stream.finished. |
node:stream/consumers, focused
All consumers present:
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.
| Export | Semantics |
|---|---|
EventEmitter | on/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, addAbortListener | Present. |
captureRejections, captureRejectionSymbol, errorMonitor, defaultMaxListeners, usingDomains | Statics present. captureRejectionSymbol and errorMonitor are real Symbols, matching Node. |
EventEmitterAsyncResource | Present as a constructor. |
init | Internal-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.
node:timers
| Export | Semantics |
|---|---|
setTimeout / clearTimeout | Fires after the delay; cleared handles never fire. Handles are objects. |
setInterval / clearInterval | Present and functional. |
setImmediate / clearImmediate | setImmediate callbacks fire before same-tick setTimeout(fn, 1) timers, as in Node. |
promises | The timers/promises namespace. |
node:timers/promises
| Export | Semantics |
|---|---|
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). |
scheduler | scheduler.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.