🌍 Environment Support

Our library is designed to be runtime agnostic: utilities target standard JavaScript capabilities instead of branching on named platforms.

📊 Compatibility Model

If a runtime provides the JavaScript and Web Platform APIs a utility needs, that utility can run there. This keeps @opentf/std portable across Node.js, Bun, Deno, browsers, edge workers, embedded engines such as QuickJS, and future runtimes without adding platform-specific detection helpers.


✅ Verified Runtime Support

We don't just claim portability — we measure it. The entire test suite is bundled and executed on each runtime below, and these are the raw results.

Where a runtime falls short, the cause is listed so you can tell the difference between "this runtime is missing an API" and "this library is broken". In every case below, the gap is in the runtime, not in @opentf/std.

Measured on 2026-08-01 against @opentf/std@0.16.0, running the full suite of 1502 tests on each runtime.

RuntimeVersionTests passingStatus
Node.js 2020.20.21501 / 1502 (99.9%)⚠️ Partial
Node.js 2222.23.11501 / 1502 (99.9%)⚠️ Partial
Node.js 2424.18.01502 / 1502 (100%)✅ Fully supported
Node.js 2626.5.11559 / 1559 (100%)✅ Fully supported
Bun1.3.141502 / 1502 (100%)✅ Fully supported
Deno2.9.41559 / 1559 (100%)✅ Fully supported
ES-Runtime0.13.01559 / 1559 (100%)✅ Fully supported
LLRT0.8.0-beta1462 / 1502 (97.3%)⚠️ Partial

Node.js 20 20.20.2

Missing platform API — Intl.DurationFormat unavailable (1 test)

Localised duration text needs Intl.DurationFormat and Intl.RelativeTimeFormat. Where either is absent Duration falls back to English rather than throwing, so only the wording differs — and format is locale-independent and unaffected. A failure here means the runtime has the API but not the locale data behind it.

Affected utilities: Duration. Everything else works as expected.

Node.js 22 22.23.1

Missing platform API — Intl.DurationFormat unavailable (1 test)

Localised duration text needs Intl.DurationFormat and Intl.RelativeTimeFormat. Where either is absent Duration falls back to English rather than throwing, so only the wording differs — and format is locale-independent and unaffected. A failure here means the runtime has the API but not the locale data behind it.

Affected utilities: Duration. Everything else works as expected.

LLRT 0.8.0-beta

Missing platform API — Intl.DateTimeFormat names unavailable (4 tests)

Month and weekday names come from Intl.DateTimeFormat, which a runtime built without the name tables still exposes while silently ignoring the month and weekday options and returning a numeric date. Only the MMM, MMMM, EEE and EEEE tokens are affected; every other token is computed arithmetically and is unaffected, as is the rest of DateTime. Supplying the names ourselves would mean bundling the locale data the module exists to avoid.

Affected utilities: DateTime. Everything else works as expected.

Missing platform API — Intl.DurationFormat unavailable (2 tests)

Localised duration text needs Intl.DurationFormat and Intl.RelativeTimeFormat. Where either is absent Duration falls back to English rather than throwing, so only the wording differs — and format is locale-independent and unaffected. A failure here means the runtime has the API but not the locale data behind it.

Affected utilities: Duration. Everything else works as expected.

Runtime spec deviation (4 tests)

Runtime does not perform IteratorClose when a for await...of loop exits early, so a finally block in an async generator never runs. Required by ECMA-262; the equivalent sync for...of path is handled correctly. The fix belongs in the runtime. For streamToIter this means a break neither cancels the stream nor releases the reader lock, so the stream stays locked — drain it rather than exiting early on this runtime.

Affected utilities: fromIterAsync, streamToIter, zipIterAsync. Everything else works as expected.

Missing platform API — Intl.NumberFormat unavailable (8 tests)

Number formatting needs Intl.NumberFormat. Runtimes built without ICU cannot format compact notation, currencies, or a plain number with grouping separators.

Affected utilities: formatCompact, formatCurrency, formatNumber. Everything else works as expected.

Runtime spec deviation — TextDecoder stream: true unavailable (1 test)

Runtime TextDecoder ignores the stream: true option, so the trailing bytes of a multi-byte character split across two chunks are decoded as replacement characters instead of being held back for the next call. Required by the Encoding spec; the fix belongs in the runtime. Text arriving in whole characters per chunk is unaffected.

Affected utilities: streamToText, streamToLines. Everything else works as expected.

Missing platform API — Intl.ListFormat unavailable (1 test)

Localised list joining needs Intl.ListFormat. Where it is absent formatList falls back to the English forms rather than throwing, so only the language differs and the shape of the output is unchanged — every other formatList test passes on such a runtime. Only this one asserts a non-English result.

Affected utilities: formatList. Everything else works as expected.

Missing platform API — Intl.Segmenter unavailable (20 tests)

Grapheme segmentation needs Intl.Segmenter. Runtimes built without full ICU cannot count, reverse or wrap multi-codepoint characters correctly. Text that is entirely printable ASCII is measured without segmenting and works everywhere.

Affected utilities: stringWidth, stringReverse, wordWrap. Everything else works as expected.


🛠️ Standard Web APIs

Most utilities are pure JavaScript. The few that need platform APIs use standard, cross-runtime Web APIs — never runtime-specific modules:

  • Crypto helpers (random, UUID, hash, HMAC) use the Web Crypto API via globalThis.crypto.

  • They throw a clear error if globalThis.crypto is unavailable.

globalThis.crypto is available out of the box in every runtime @opentf/std targets — Node.js ≥ 20, Bun, Deno, modern browsers, and edge workers — so no polyfill or runtime branching is required.

Temporal and DateTime

DateTime uses the TC39 Temporal API where the runtime provides it, and Intl.DateTimeFormat everywhere else. Both backends implement identical semantics and are held to that by a differential test, so the API behaves the same regardless of which one is active.

RuntimeTemporalDateTime backend
Node.js 26+YesTemporal
DenoYesTemporal
ES-RuntimeYesTemporal
Chrome 144+, Edge 144+YesTemporal
Firefox 139+YesTemporal
Node.js 24 / 25⚠️ --harmony-temporal onlyIntl unless flagged
Node.js 22 and earlier, incl. 20 LTSNoIntl
Bun, LLRTNoIntl
SafariNoIntl

Temporal is now the majority position in browsers, and Node.js 26 enables it by default. The Intl backend still matters: Node.js 20 LTS and 22, Bun and LLRT have no Temporal at all, so it remains what a large share of server-side code runs on.

Revisions also differ. The Temporal in Node.js 24/25 predates timeZoneId and exposes a timeZone object instead; DateTime.fromTemporal reads both, so a zone is never silently lost. Zone spellings can differ between revisions too — an older ICU may report Asia/Calcutta where a newer one reports Asia/Kolkata. Both are the same zone and behave identically.

toTemporal() and DateTime.fromTemporal() are the only members that require it; they throw a clear error where it is absent, so check DateTime.hasTemporal first.

Time-zone support relies only on Intl.DateTimeFormat accepting a timeZone and exposing formatToParts, which every target runtime provides — including LLRT. No time-zone database is bundled.

On LLRT, Intl ignores locales and component options, so DateTime month names, weekday names and toLocaleString do not work there. Numeric format tokens, time-zone handling and all date arithmetic are unaffected. This is the same ICU limitation that affects formatCompact and formatCurrency.

Info

@opentf/std does not expose public runtime detection helpers. Prefer checking for the capability your code needs, not the runtime name.


🚀 Future-Proof Design

The @opentf/std library is built using modern ESM standards and leverages native platform APIs wherever possible. By choosing this library, you are ensuring that your code:

  • Runs Anywhere Capabilities Exist: Seamlessly move logic between edge, server, browser, and embedded runtimes.

  • Remains Lightweight: Zero external dependencies and full tree-shaking support.

  • Stays Modern: We build on the latest JavaScript features and stable, standard platform APIs.

Last updated on
Edit this page