Modern APIs
Reference for the JavaScript, web-platform, and Node.js APIs that Nub supplies across supported Node versions.
Nub fills gaps between supported Node versions so application code can use one modern API surface. The reference below describes what Nub adds, how it is delivered, and when Node's native implementation takes over.
Nub's types package declares the keyed Promise combinators, Temporal, the browser-style Worker global, and reportError. Standard APIs use TypeScript's bundled libraries and @types/node.
# install the package
npm i -D @nubjs/types @types/node@25
# tsconfig.json
{ "compilerOptions": { "types": ["node", "@nubjs/types"] } }How augmentation works
Nub augments the stock Node binary through Node's extension surfaces. It does not patch Node, replace V8, or ship a fork.
- JavaScript polyfills run from Nub's preload and feature-detect each property before defining it. They fill missing globals and methods without replacing native or user-supplied implementations.
- Lazy globals defer larger fallbacks until first use. On older Node versions, reading
Temporalloads the fallback and replaces the getter with the resulting namespace. - Semantic patches correct a narrow behavior on versions where an API exists but does not yet match the standard. They are also feature-detected.
- Experimental flags expose APIs already built into a particular Node release. Nub derives flags from the exact Node version because passing an unknown flag would abort Node during startup.
- Loader hooks add supported import formats through Node's module-loader API. They do not emulate native subsystems that are absent from the running Node.
Native implementations always win. As Node adds an API, Nub's feature check steps aside; application code keeps the same surface while the delivery mechanism changes underneath it.
Compatibility mode disables the entire augmentation layer. Run with nub --node or set NODE_COMPAT=1 to use the project's pinned Node with no polyfills, preload patches, loader hooks, or automatically enabled flags.
Reference
Availability below covers Nub's supported Node range, beginning with Node 18.19. A native-version note says when Nub can step aside, not when application code may first use the API under Nub.
JavaScript APIs
Temporal
Temporal provides immutable types for exact instants, calendar values, durations, and time-zone-aware values. Date.prototype.toTemporalInstant() converts an existing Date to the equivalent instant.
Delivery
Lazy global polyfill on Node 18–25; native on Node 26+. Nub loads @js-temporal/polyfill only when code first reads Temporal and routes direct imports of that package to the same namespace.
const deployed = Temporal.Instant.from("2026-08-07T16:30:00Z");
const local = deployed.toZonedDateTimeISO("America/Los_Angeles");
const fromDate = new Date("2026-08-07T16:30:00Z").toTemporalInstant();
console.log(local.add({ days: 1 }).toString());
console.log(fromDate.equals(deployed)); // truePromise.allKeyed()
The keyed Promise combinators accept an object and return a null-prototype object with the same own enumerable string and symbol keys. Values pass through Promise.resolve; allKeyed rejects on the first rejection, while allSettledKeyed records every outcome.
Delivery
Feature-detected JavaScript polyfill on every supported Node version. No Node release ships the await-dictionary proposal yet.
const { account, invoices } = await Promise.allKeyed({
account: getAccount(accountId),
invoices: listInvoices(accountId),
});
const outcomes = await Promise.allSettledKeyed({
profile: getProfile(account.id),
avatar: getAvatar(account.id),
});Promise.withResolvers()
Promise.withResolvers() creates a promise with its resolve and reject functions. Promise.try() invokes a callback and turns either its return value or thrown error into a promise outcome.
Delivery
Feature-detected JavaScript polyfills. Promise.withResolvers() is native on Node 22+; Promise.try() is native on Node 24+.
const { promise, resolve } = Promise.withResolvers<string>();
queueMicrotask(() => resolve("ready"));
const config = await Promise.try(() => JSON.parse(source));
console.log(await promise, config);Iterator helpers
Iterator helpers transform and consume iterators lazily without first materializing an array.
Delivery
Feature-detected JavaScript polyfill before Node 22; native on Node 22+.
const activeNames = Iterator.from(users)
.filter((user) => user.active)
.map((user) => user.name)
.take(10)
.toArray();Helper identity on older Node
The fallback returns generator-backed helper objects. Iteration, laziness, validation, and iterator closing match the proposal, but the prototype and Symbol.toStringTag differ from native iterator-helper objects.
The supplied helpers are map, filter, take, drop, flatMap, reduce, toArray, some, every, find, and forEach.
Iterator.concat()
Iterator.concat() lazily visits each iterable in sequence.
Delivery
Feature-detected JavaScript polyfill before Node 26; native on Node 26+.
const allIds = Iterator.concat(primaryIds, archivedIds).toArray();Iterator.zip()
The joint-iteration methods advance several iterables together. zip() yields arrays, while zipKeyed() preserves the keys of an input object.
Delivery
Feature-detected JavaScript polyfill on every supported Node version. No Node release ships these methods yet.
const pairs = Iterator.zip([ids, names]).toArray();
for (const row of Iterator.zipKeyed({ id: ids, name: names })) {
console.log(row.id, row.name);
}Iterator.prototype.chunks()
These helpers batch an iterator, produce overlapping windows, search for a value, or join values into a string.
Delivery
Feature-detected JavaScript polyfill on every supported Node version. No Node release ships these methods yet.
const batches = Iterator.from(records).chunks(100).toArray();
const movingTriples = Iterator.from(values).windows(3).toArray();
const hasDraft = Iterator.from(statuses).includes("draft");
const label = Iterator.from(tags).join(", ");Set.prototype.union()
The Set methods provide standard union, intersection, difference, relationship, and overlap operations.
Delivery
Feature-detected JavaScript polyfill before Node 22; native on Node 22+.
const visible = owned.union(shared).difference(hidden);
const overlap = selected.intersection(available);
if (required.isSubsetOf(visible) && blocked.isDisjointFrom(visible)) {
render(visible);
}The complete family is union, intersection, difference, symmetricDifference, isSubsetOf, isSupersetOf, and isDisjointFrom.
Map.prototype.getOrInsert()
The insertion methods return an existing value or insert one when the key is absent. The computed form calls its callback only for a missing key. Both methods are also available on WeakMap.
Delivery
Feature-detected JavaScript polyfill before Node 26; native on Node 26+.
const cache = new Map<string, Result>();
const result = cache.getOrInsertComputed(key, () => calculate(key));Object.groupBy()
The grouping methods partition an iterable by a key returned from a callback.
Delivery
Feature-detected JavaScript polyfill before Node 21; native on Node 21+.
const byStatus = Object.groupBy(tasks, (task) => task.status);
const byOwner = Map.groupBy(tasks, (task) => task.owner);Array.prototype.toSorted()
The copying methods produce an updated array without mutating the original. Typed arrays support toSorted, toReversed, and with, but not toSpliced.
Delivery
Feature-detected JavaScript polyfill before Node 20; native on Node 20+.
const ranked = scores.toSorted((a, b) => b - a);
const corrected = values.with(2, replacement);
const withoutFirst = values.toSpliced(0, 1);
const reversed = values.toReversed();String.prototype.isWellFormed()
These methods detect or replace lone UTF-16 surrogates.
Delivery
Feature-detected JavaScript polyfill before Node 20; native on Node 20+.
if (!input.isWellFormed()) {
input = input.toWellFormed();
}ArrayBuffer.prototype.transfer()
ArrayBuffer transfer moves the bytes into a new buffer and detaches the original.
Delivery
Feature-detected JavaScript polyfill before Node 21; native on Node 21+.
const source = new ArrayBuffer(1024);
const resized = source.transfer(2048);
console.log(source.detached); // true
console.log(resized.byteLength); // 2048Array.fromAsync()
Array.fromAsync() builds an array from an async iterable, optionally mapping each value as it arrives.
Delivery
Feature-detected JavaScript polyfill before Node 22; native on Node 22+.
const rows = await Array.fromAsync(stream, (row) => normalize(row));URL.parse()
URL.parse() returns a parsed URL or null instead of throwing for invalid input.
Delivery
Feature-detected JavaScript polyfill on Node versions without the method. It is native on Node 20.19–20.x and Node 22.1+.
const callback = URL.parse(value, "https://example.com");
if (callback) console.log(callback.pathname);RegExp.escape()
RegExp.escape() quotes a string for literal use inside a regular expression.
Delivery
Feature-detected JavaScript polyfill before Node 24; native on Node 24+.
const exactName = new RegExp(`^${RegExp.escape(name)}$`, "u");Error.isError()
Error.isError() identifies Error objects without relying on a mutable prototype property.
Delivery
Feature-detected JavaScript polyfill before Node 24; native on Node 24+.
try {
await runTask();
} catch (error) {
if (Error.isError(error)) console.error(error.message);
}Cross-realm errors on older Node
On Node 23 and below, the userland fallback cannot recognize an Error created in another realm because V8's internal Error slot is not exposed to JavaScript.
Float16Array
The half-precision float APIs store and round IEEE 754 binary16 values.
Delivery
Feature-detected JavaScript polyfill before Node 24; native on Node 24+.
const samples = new Float16Array([0.1, 0.2, 0.3]);
const rounded = Math.f16round(1 / 3);
const view = new DataView(new ArrayBuffer(2));
view.setFloat16(0, rounded);
console.log(view.getFloat16(0));ArrayBuffer views on older Node
On Node 23 and below, ArrayBuffer.isView() cannot recognize a polyfilled Float16Array because JavaScript cannot create V8's internal typed-array slot.
Uint8Array.prototype.toBase64()
The encoding methods convert bytes to and from base64, base64url, and hexadecimal text. The setFrom variants decode into an existing array and report how much input was read and how many bytes were written.
Delivery
Feature-detected JavaScript polyfill before Node 25; native on Node 25+.
const bytes = new TextEncoder().encode("hello");
const encoded = bytes.toBase64({ alphabet: "base64url", omitPadding: true });
const decoded = Uint8Array.fromBase64(encoded, { alphabet: "base64url" });
console.log(decoded.toHex());The complete family is toBase64, fromBase64, setFromBase64, toHex, fromHex, and setFromHex.
DisposableStack
Explicit Resource Management collects cleanup callbacks and disposable resources, then releases them in reverse order.
Delivery
Feature-detected JavaScript polyfill before Node 24; native on Node 24+.
const stack = new DisposableStack();
stack.defer(() => releaseLock());
stack.adopt(openHandle(), (handle) => handle.close());
try {
await work();
} finally {
stack.dispose();
}Math.sumPrecise()
Math.sumPrecise() reduces floating-point error and avoids overflow caused only by an unfortunate addition order.
Delivery
Feature-detected JavaScript polyfill on every supported Node version. No Node release ships this method yet.
const total = Math.sumPrecise([1, 1e100, 1, -1e100]);
console.log(total); // 2Symbol.metadata
Decorators use Symbol.metadata as the key for class metadata.
Delivery
Feature-detected JavaScript polyfill on every supported Node version. No Node release ships this symbol yet.
const metadata = Example[Symbol.metadata];Atomics.pause()
Atomics.pause() provides a hint inside short spin-wait loops.
Delivery
Feature-detected JavaScript polyfill on every supported Node version. No Node release ships this method yet; the fallback validates its argument and otherwise acts as a no-op.
while (Atomics.load(state, 0) === 0) {
Atomics.pause();
}Web APIs
URLPattern
URLPattern matches URL components and exposes named groups.
Delivery
Feature-detected global polyfill before Node 24; native on Node 24+.
const route = new URLPattern({ pathname: "/users/:id" });
const match = route.exec("https://example.com/users/42");
console.log(match?.pathname.groups.id); // 42File
File represents named Blob data with a media type and modification time.
Delivery
Feature-detected global polyfill on Node 18 and 19; native on Node 20+.
const file = new File([JSON.stringify(payload)], "payload.json", {
type: "application/json",
});navigator
The global navigator exposes server-relevant runtime information such as hardwareConcurrency and userAgent.
Delivery
Feature-detected global shim before Node 21; native on Node 21+. Nub adds only missing properties and preserves any existing navigator object.
const workers = Math.max(1, navigator.hardwareConcurrency - 1);
console.log(navigator.userAgent, workers);navigator.locks
The Web Locks API coordinates asynchronous access to a named resource within the process.
Delivery
Feature-detected Web Locks implementation before Node 24.5; native on Node 24.5+.
await navigator.locks.request("database-migration", async () => {
await migrate();
});Worker
Nub exposes a browser-style Worker global backed by Node worker threads, including web events, message channels, transfer lists, TypeScript entrypoints, and worker names.
Delivery
Preload-installed global on every supported Node version. Node does not ship a browser-style global Worker; Nub adapts node:worker_threads without replacing that module.
const worker = new Worker(new URL("./worker.ts", import.meta.url), {
name: "thumbnailer",
});
worker.onmessage = ({ data }) => console.log(data);
worker.postMessage({ source: "photo.jpg" });See Web Workers for lifecycle events, worker options, module loading, and Node interop.
localStorage
The storage globals provide synchronous string key/value storage using Node's built-in Web Storage implementation.
Delivery
Version-gated Node API available on Node 22.4+. Nub enables the experimental flag before Node 25, but a storage file is still required; Nub does not emulate the API on older Node.
localStorage.setItem("theme", "dark");
const theme = localStorage.getItem("theme");
sessionStorage.setItem("request-id", crypto.randomUUID());See Web Storage for configuring the storage file and choosing a scope.
reportError()
reportError() reports an exception through the host's uncaught-error path without throwing it at the call site.
Delivery
Feature-detected global polyfill on every supported Node version. No Node release ships this global yet.
backgroundTask().catch((error) => {
reportError(error);
});WebSocket
The WebSocket global creates standards-compatible client connections.
Delivery
Version-gated Node API available on Node 20.10+. Nub enables Node's experimental implementation before Node 22; it is native without a flag on Node 22+. Nub does not emulate it on earlier releases.
const socket = new WebSocket("wss://example.com/events");
socket.addEventListener("message", ({ data }) => console.log(data));EventSource
EventSource consumes a server-sent event stream.
Delivery
Version-gated Node API available on Node 20.18–20.x and Node 22.3+. It is unavailable on Node 21 and remains experimental through Node 26.
const events = new EventSource("https://example.com/events");
events.addEventListener("update", ({ data }) => console.log(data));MessageEvent.ports
Transferred message ports are exposed through a read-only array, matching the web-platform contract.
Delivery
Semantic preload patch on older Node versions whose MessageEvent.ports value is mutable. Nub leaves conforming native behavior unchanged.
channel.port1.onmessage = (event) => {
const [transferredPort] = event.ports;
transferredPort?.postMessage("received");
};Node.js APIs and loading behavior
These entries expose parsers, modules, and flags already present in particular Node releases. Nub does not polyfill a missing native subsystem.
vm.Module
The VM module classes compile and link ECMAScript modules inside a VM context.
Delivery
Experimental-flag enablement on every supported Node version. Nub passes --experimental-vm-modules automatically.
import vm from "node:vm";
const module = new vm.SourceTextModule("export const answer = 42");
await module.link(() => { throw new Error("no imports expected"); });
await module.evaluate();WebAssembly module imports
WebAssembly modules can be imported through the ESM loader.
Delivery
Node feature enabled automatically across the supported range. Nub passes the experimental flag on Node 18.19–22.18 and 23.0–24.4; the feature is unflagged on Node 22.19–22.x and Node 24.5+.
import { add } from "./math.wasm";
console.log(add(20, 22));node:sqlite
Node's built-in SQLite module provides synchronous database access without an npm dependency.
Delivery
Version-gated Node API available on Node 22.5+. Nub enables its experimental flag before Node 22.13 and 23.4; it is unflagged afterward.
import { DatabaseSync } from "node:sqlite";
const database = new DatabaseSync("app.db");
const row = database.prepare("SELECT count(*) AS count FROM users").get();Native addon imports
Native .node addons can be imported directly from an ES module.
Delivery
Version-gated Node API available on Node 22.20–22.x and Node 23.6+. Nub enables --experimental-addon-modules; the feature remains experimental through Node 27.
import addon from "./build/Release/addon.node";
console.log(addon.version());Text imports
The type: "text" import attribute loads a file as a string.
Delivery
Loader-hook fallback from Node 18.20 onward, or Node's flagged implementation on Node 24.19–24.x and 26.5+. Nub uses the native parser where available.
import template from "./email.html" with { type: "text" };
console.log(template.length);node:stream/iter
The stream iterator module provides iterator-oriented stream utilities.
Delivery
Version-gated Node API available on Node 25.9+. Nub enables --experimental-stream-iter; it does not emulate the module on older releases.
import { from, text } from "node:stream/iter";
const source = from("Hello, world!");
console.log(await text(source));node:ffi
The FFI module calls functions in native dynamic libraries.
Delivery
Version-gated Node API available on Node 26.1+. Nub enables --experimental-ffi; permission flags remain the application's choice.
import { dlopen } from "node:ffi";
const { lib, functions } = dlopen("./libmath.so", {
add_i32: { arguments: ["i32", "i32"], return: "i32" },
});
console.log(functions.add_i32(20, 22));
lib.close();node:vfs
The VFS module exposes Node's virtual filesystem primitives.
Delivery
Version-gated Node API available on Node 26.4+. Nub enables --experimental-vfs; it does not emulate the module on older releases.
import vfs from "node:vfs";
const memory = vfs.create();
memory.mkdirSync("/data", { recursive: true });
memory.writeFileSync("/data/message.txt", "hello");
console.log(memory.readFileSync("/data/message.txt", "utf8"));Module syntax detection
Syntax detection lets Node classify ambiguous .js input as ESM when it contains module syntax.
Delivery
Version-gated Node behavior available on Node 20.10+ except Node 21.0. Nub enables the flag before it became default in Node 20.19 and 22.7.
// package.json has no "type" field; Node detects this file as ESM.
export const answer = 42;