Roadmap

What is not in Lumen yet, and why. Everything shipped so far is in the standard library reference, each function with a stability pill; this page is the other half of that list.

How Lumen grows

The surface grows in explicit, conformance-backed slices: a feature lands with its checker rule, its emitter, its runtime support, a spec folder and a conformance case, or it does not land. Names follow the conventional Node and TypeScript ones so that what you already know keeps working. Each module below lists the APIs still missing and the concrete reason, which is usually a language feature the module is waiting on rather than a lack of interest.

Progress is visible on the releases page and in the issue tracker. If something here blocks you, say so on an issue: the order is driven by real programs.

Language-level gaps the modules wait on

GapWhat it unblocks
Growable arrays (push, pop, sort)the remaining Array methods, Buffer.concat, simpler collection code
A general Stream abstractionprocess.stdout-style objects, Console instances, streaming zlib, streaming HTTP bodies, child_process.spawn
A locking primitive (a Mutex-shaped type)safe shared state across a concurrent http.createServer and across Worker threads
A cross-thread message channelnew Worker(...) with postMessage, worker pools, non-scalar results from Worker.run
Async on the wasm targetasync/await, timers and Worker.run in the browser playground
Signal delivery into user codeprocess.on('SIGINT', …) and friends

Standard library, module by module

console

APINotes
console.assert(cond, msg) · console.time(label)/timeEnd(label) · console.count(label)/countReset(label)real, scoped follow-up (small global timer/counter state in the generated runtime); not attempted in the pass that shipped warn/info/debug/trace
console.group/groupEndneeds indentation state threaded through every log call site, a bigger, cross-cutting change
console.table · console.dirneed structured/tabular object formatting; today's printFormat is a flat per-type format-string lookup
console.clearterminal-control-code territory, low value
Console class / custom-stream constructor (new Console(stdout, stderr))needs a Stream abstraction Lumen doesn't have (the same gap blocking process.stdout/stdin/stderr)

fs

APINotes
fs.promises.* / most other async callback variants (fs.rename, fs.chmod, fs.readdir, ... most of Node's ~54)fs.readFile/writeFile/appendFile are true async (io_uring, no thread); fs.unlink/mkdir/rmdir/stat are thread-pool-backed (spec 047) -- every other one needs the same thread-pool wrapping, mechanically the same pattern now that it's proven, just not done yet
fs.watch recursive watching, non-Linux platforms; fs.watchFile/unwatchFilefs.watch(path, listener) itself and its create/change/rename event-type distinction shipped (Linux, flat, one filename per event); each of these is a real, separate follow-up
fs.opendirSync(path) · fs.globSync(pattern)need a directory-iterator class / a real glob algorithm
fs.statfsSync(path)filesystem-level stats; platform-specific, low value for now
fs.openAsBlob(path)no Blob type in the language

path

APINotes
path.relative(from, to)not attempted yet, though path.resolve shipping real cwd access removes what used to be the blocker -- a real, separate follow-up now, not a new gap
path.matchesGlob(path, pattern)needs a real glob algorithm
path.win32 / path.posix / path.toNamespacedPathLumen targets POSIX only

process

APINotes
process.stdout / stdin / stderrno Stream abstraction in the language
signal events, process.on(...)process.kill() can send a signal now, but nothing can receive one back into user code — no event/listener infrastructure yet
process.nextTick(fn)needs a design decision about what happens when it's called in a program with no other async machinery running (a plain sync-looking program never starts the event loop today)
process.resourceUsage() · process.cpuUsage() · process.threadCpuUsage()no per-thread/per-process CPU-time accounting primitive vetted yet; memoryUsage() covers the highest-value single field
process.send() / .disconnect() / .channel (IPC)no persistent child-process channel — child_process.spawnSync is a synchronous one-shot
process.report.* · .permission.* · .finalization.*advanced/niche Node-internals surface, out of scope
process.versions · .release · .config · .features.*Node-build-metadata specific (V8/OpenSSL/ICU versions); not meaningful here. process.version() itself ships as a distinct Lumen-specific marker
process.title · .execPath · .argv0 · .mainModule · .dlopen · .execveniche/process-replacement-level operations

os

APINotes
os.cpus()needs a record array parsed from /proc/cpuinfo and /proc/stat; no single-syscall shortcut
os.networkInterfaces()needs interface enumeration (a getifaddrs equivalent)
os.userInfo()needs a passwd-database lookup, the same libc gap as homedir()'s Node fallback
os.getPriority() · os.setPriority()no wrapped primitive available; would need a raw syscall number
os.constantslow value without the functions that consume it

crypto

APINotes
crypto.createCipheriv/streaming AEADa stateful encrypt/decrypt object is a real, separate shape (associated-data ordering, partial-block buffering) from createHash/createHmac's pure hash accumulator (spec 060); not attempted yet
crypto.sign()/verify()asymmetric/keyed crypto, a much larger surface than this milestone's scope
crypto.pbkdf2() · crypto.scrypt()deliberately deferred rather than rushed; picking the right default cost parameters deserves its own pass
Algorithms beyond md5/sha1/sha256/sha512createHash/createHmac (spec 060) cover those four; sha224/sha384/blake2/blake3/etc. are a real, separable expansion once there's a concrete need

url

APINotes
a real URL class / .searchParams / .toString()Node's modern API is class-based; the older function-based parse/format fit the static-function stdlib pattern better for v1
username/password/auth fieldsniche; straightforward to add later
relative-to-base URL resolution (new URL(relative, base))a separate feature from parsing one string

child_process

APINotes
exec/execSync (shell-string form)needs shell-quoting/escaping logic to be safe; spawnSync's array-of-args form sidesteps this for v1
spawn (async, streaming stdio)needs the same event-loop integration the async fs trio got, plus a design for streaming stdio
cwd/env/timeout optionsthe underlying primitive already supports these; just not exposed as spawnSync parameters yet
stdin piping · signal-based exit statusspawnSync's one-shot model doesn't need stdin; no optional-string-or-int union exists yet to represent a signal-based exit

Array (more)

APINotes
a.push/pop/sortneeds growable arrays

http

APINotes
Idle keep-alive connection timeoutsa connection stays open indefinitely as long as the client keeps sending requests and doesn't send Connection: close
Response headers on the client sidehttp.request's own request headers and http.createServer's response headers both shipped, via Map<string, string>; reading real response headers on the client needs a lower-level request flow than what's wired up yet -- confirmed reachable, deliberately deferred rather than rewriting an already-working, already-benchmarked call under time pressure
Case-insensitive header lookup, repeated header/query values as an arrayMap<string, string>'s exact-match, last-write-wins lookup is what's available now
A general-purpose locking primitive for handler-shared stateconcurrent serving (shipped) means a handler now genuinely runs on multiple OS threads; a handler that mutates shared global state without its own synchronization has a real data race. Map/Set now detect an overlapping access and fail loudly instead of corrupting or crashing unpredictably (spec 492), but that is a diagnostic, not a fix -- a handler still cannot safely share mutable state across a concurrent server's connections, since Lumen has no Mutex-shaped stdlib type yet
Real Server/IncomingMessage/ServerResponse/ClientRequest/Agent classesNode's classes bundle request/response data with an event mechanism; EventEmitter alone doesn't supply the data half, which still needs the header-collection/streaming gaps closed first
Server lifecycle events ('error', 'close') via EventEmitter<T>genuinely reachable now that EventEmitter exists — a real follow-up
Streaming request/response bodiesneeds a network-backed ReadableStream/WritableStream (spec 046 shipped file-backed streams only, a deliberately separate backing type -- see spec 046's "why file-backed only")
HTTPS/TLS-specific configurationstd.http.Client already handles basic TLS internally for https:// URLs; exposing it as configurable is a real, separate feature, disproportionately large for a single pass
WebSocket upgradea full protocol implementation (handshake, frame parsing/masking, ping/pong) -- a separate feature on the scale of http itself, not an extension of it

zlib

APINotes
zlib.gzip/unzip (async, callback or Promise-based)every other module's *Sync functions have shipped first before their async twins get a dedicated pass (see fs.readFileSync predating fs.readFile's thread-pool design); compression is comparatively rare on a hot path, sync-first is the right order here too
zlib.createGzip()/createDeflate() streaming transformneeds a stateful stream/duplex object the language doesn't have yet; one-shot whole-buffer compression covers the common case
Brotli (zlib.brotliCompressSync/etc.)not in std.compress at all in this Zig version (only flate, zstd, lzma/lzma2, xz exist) — would need vendoring a C library or a from-scratch implementation
zstd/xz/lzma wrappersstd.compress ships these too, but Node's own zlib module only covers gzip/deflate/brotli — out of scope for a module matching that name/surface
A compression-level parameterthe stdlib's wrappers so far are plain positional-arg functions (see crypto.randomBytes(n)), not an options-object style; can be added as an optional second argument later

Buffer

APINotes
crypto.* returning Buffer instead of hex/base64 stringsa real audit of every existing crypto call site and its current string-returning contract -- a breaking change to an already-shipped API, deliberately not bundled into the pass that first introduces the type it would return
ReadableStream/WritableStream chunks as Buffer instead of stringspec 046 already shipped and is exercised as string-chunked; switching its chunk type is a separate, real migration
JSON (de)serialization of Bufferno existing convention in this codebase for how a byte array should round-trip through JSON (Node uses {"type":"Buffer","data":[...]}); not decided here
Buffer.concat([...])needs a decision on how a Lumen array of Buffer values is spelled (Buffer[] -- an untested combination of a new bare type with array-of syntax) before it's worth building
buf[i] index syntaxthe indexing checker is array-type-specific; .at(i) covers the same need today

Worker

APINotes
new Worker(...) with postMessage/on('message')/terminate()needs a real cross-thread mutable message channel Lumen has no primitive for yet; shipping the constructor shape without the instance methods it implies would be worse than the flat Worker.run shape actually shipped
Non-scalar T (strings, arrays, Map/Set/Buffer, objects, class instances) crossing the boundarynot verified safe this pass without a real capture/deep-copy-semantics analysis; scalars alone are enough to prove the thread-spawn + promise-handback mechanism works correctly
Static prevention of unsafe reference-shaped captureswould need a real capture/Send-style analysis in the checker; documented as a known hazard instead, the same way http.createServer's multi-threaded-handler trade-off above is documented rather than solved
A persistent worker pool / reusable Worker handleno concrete use case needs one yet; one detached thread per Worker.run() call is simpler and sufficient
Worker cancellation / timeoutsnot exercised by the v1 function set — each call is a single run-to-completion function, no cancellation hook exists to wire up
--wasm supportWorker.run requires the async/Promise event-loop machinery, which already hits the existing wasm-vs-@import("xev") gate — wasm32-wasi has no real OS threads anyway