node: support Node.js 26.4 and later (#1410)

Keep 26.4.0 as the minimum supported version while allowing later
releases.
This commit is contained in:
Simon Klee
2026-08-23 06:56:30 +02:00
committed by GitHub
parent e3937cf0dd
commit fa14b96ad4
26 changed files with 148 additions and 81 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ concurrency:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
NODE_VERSION: 26.4.0
NODE_VERSION: 26.7.0
ZIG_VERSION: 0.16.0
jobs:
+1 -1
View File
@@ -15,7 +15,7 @@ concurrency:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
NODE_VERSION: 26.4.0
NODE_VERSION: 26.7.0
jobs:
test:
+1 -1
View File
@@ -15,7 +15,7 @@ concurrency:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
NODE_VERSION: 26.4.0
NODE_VERSION: 26.7.0
jobs:
bun-test:
+1 -1
View File
@@ -15,7 +15,7 @@ concurrency:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
NODE_VERSION: 26.4.0
NODE_VERSION: 26.7.0
jobs:
test:
+1 -1
View File
@@ -22,7 +22,7 @@ permissions:
id-token: write
env:
NODE_VERSION: 26.4.0
NODE_VERSION: 26.7.0
jobs:
publish:
+1 -1
View File
@@ -10,7 +10,7 @@ permissions:
contents: write
env:
NODE_VERSION: 26.4.0
NODE_VERSION: 26.7.0
jobs:
# Extract version and check if it's a dry run
+2 -2
View File
@@ -15,8 +15,8 @@
- Use Bun for dependency management and development commands: `bun install`, `bun run <script>`, `bun test`, and `bun <file>`.
- Run package scripts from the package directory unless the script is defined at the repository root.
- Shared runtime code must preserve the supported Bun and Node paths. Keep runtime-specific behavior behind existing
platform/runtime/build seams; do not introduce Bun-only APIs into shared modules. Node checks use the version enforced
by `scripts/node26.mjs`.
platform/runtime/build seams; do not introduce Bun-only APIs into shared modules. Node checks use the minimum
version enforced by `scripts/node26.mjs`.
## Verification
@@ -28,7 +28,7 @@ Calibration failures retry a complete pair. Retained timing and pair-gap drift a
## Run
From `packages/core`, set `NODE26_PATH` to Node 26.4.0:
From `packages/core`, set `NODE26_PATH` to Node 26.4 or later:
```sh
export NODE26_PATH=/absolute/path/to/node
@@ -5,6 +5,7 @@ import { readFileSync, rmSync, writeFileSync } from "node:fs"
import { availableParallelism, tmpdir } from "node:os"
import { dirname, join, resolve } from "node:path"
import { fileURLToPath } from "node:url"
import { isSupportedNode26Version } from "../../../../scripts/node26.mjs"
type RuntimeName = "bun" | "node"
type SuiteName = "quick" | "default" | "long"
@@ -84,7 +85,9 @@ if (unknownScenarios.length > 0) throw new Error(`unknown scenarios: ${unknownSc
if (selectedNames.length === 0) throw new Error("no benchmark scenarios selected")
const nodeVersion = readNodeVersion()
if (nodeVersion !== "v26.4.0") throw new Error(`Node v26.4.0 is required, got ${nodeVersion}`)
if (!isSupportedNode26Version(nodeVersion)) {
throw new Error(`Node v26.4.0 or later is required, got ${nodeVersion}`)
}
const raw = new Map<string, ProcessRoundSample[]>()
const scenarioMetadata = new Map<string, ChildPayload["scenario"]>()
@@ -6,6 +6,7 @@ import { existsSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSy
import { availableParallelism, cpus, loadavg, release, tmpdir } from "node:os"
import { dirname, isAbsolute, join, resolve } from "node:path"
import { fileURLToPath } from "node:url"
import { isSupportedNode26Version } from "../../../../scripts/node26.mjs"
import { getNativeAssetDescriptor, type NodeAssetTarget } from "../node-asset-target.js"
import {
analyzePairedObservations,
@@ -134,7 +135,9 @@ if (process.env.OTUI_ASSET_ROOT) {
throw new Error("unset OTUI_ASSET_ROOT so paired provenance matches the native packages being hashed")
}
const nodeRuntime = readNodeRuntime()
if (nodeRuntime.version !== "v26.4.0") throw new Error(`Node v26.4.0 is required, got ${nodeRuntime.version}`)
if (!isSupportedNode26Version(nodeRuntime.version)) {
throw new Error(`Node v26.4.0 or later is required, got ${nodeRuntime.version}`)
}
const baseline = createTarget("baseline", baselineRoot)
const candidate = createTarget("candidate", candidateRoot)
@@ -83,7 +83,9 @@ mkdirSync(attemptsDir, { recursive: true })
mkdirSync(reportsDir, { recursive: true })
const nodeRuntime = readNodeRuntime()
if (nodeRuntime.version !== "v26.4.0") throw new Error(`Node v26.4.0 is required, got ${nodeRuntime.version}`)
if (!isSupportedNode26Version(nodeRuntime.version)) {
throw new Error(`Node v26.4.0 or later is required, got ${nodeRuntime.version}`)
}
if (nodeRuntime.arch !== "x64") throw new Error(`Node x64 is required, got ${nodeRuntime.arch}`)
buildNodeScenario()
@@ -166,7 +168,7 @@ for (let attemptIndex = 0; attemptIndex < runs; attemptIndex++) {
payload.schemaVersion === 1 &&
payload.scenario.name === scenario &&
payload.sample.runtime.name === "node" &&
payload.sample.runtime.version === "v26.4.0" &&
isSupportedNode26Version(payload.sample.runtime.version) &&
payload.sample.runtime.arch === "x64" &&
payload.sample.operations > 0 &&
payload.sample.elapsedNs > 0 &&
@@ -245,6 +247,18 @@ if (
)
process.exit(1)
function isSupportedNode26Version(version: string): boolean {
const match = /^v?(\d+)\.(\d+)\.(\d+)/.exec(version)
if (match === null) return false
const major = Number(match[1])
const minor = Number(match[2])
const patch = Number(match[3])
if (major !== 26) return major > 26
if (minor !== 4) return minor > 4
return patch >= 0
}
function readNodeRuntime(): {
version: string
execPath: string
@@ -5,6 +5,7 @@ import { readFileSync, rmSync, writeFileSync } from "node:fs"
import { availableParallelism, tmpdir } from "node:os"
import { dirname, join, resolve } from "node:path"
import { fileURLToPath } from "node:url"
import { isSupportedNode26Version } from "../../../../scripts/node26.mjs"
type RuntimeName = "bun" | "node"
@@ -65,7 +66,9 @@ if (suite !== "quick" && suite !== "default" && suite !== "long") {
}
const nodeVersion = readNodeVersion()
if (nodeVersion !== "v26.4.0") throw new Error(`Node v26.4.0 is required, got ${nodeVersion}`)
if (!isSupportedNode26Version(nodeVersion)) {
throw new Error(`Node v26.4.0 or later is required, got ${nodeVersion}`)
}
const listedScenarios = listScenarios()
const scenarios = listedScenarios
+4 -2
View File
@@ -756,8 +756,10 @@ function toNodeFFIType(type: FFITypeOrString, position: "parameter" | "result"):
// Bun's N-API bridge types are not equivalent to raw Node FFI pointers.
throw new Error(NODE_NAPI_UNSUPPORTED)
case FFIType.buffer:
// Node 26.4's Linux fast-buffer trampoline can pass a null pointer for
// multi-argument signatures. The pointer path still borrows the owner.
// Keep `buffer` on the pointer path. Node 26.4's Linux fast-buffer trampoline
// can pass a null pointer for multi-argument signatures, and later Fast FFI
// still treats buffer-shaped args more tightly than pointer args. The pointer
// path still borrows the owner for the call.
return "pointer"
default:
return unsupportedNodeFFIType(type)
+2 -2
View File
@@ -39,9 +39,9 @@ npm install @opentui/ssh
```
`@opentui/core` is a peer dependency. Supported runtimes are Bun ≥ 1.3.0 and
Node.js 26.4.0. CI runs the SSH integration suite with Bun on macOS, Linux,
Node.js 26.4.0. CI runs the SSH integration suite with Bun on macOS, Linux,
and Windows, and installs, imports, starts, and closes the packed ESM package
with Node.js 26.4.0.
on Node.js.
## The shape: `createServer(config).serve(handler)`
+1 -1
View File
@@ -45,6 +45,6 @@
},
"engines": {
"bun": ">=1.3.0",
"node": "26.4.0"
"node": ">=26.4.0"
}
}
@@ -72,7 +72,7 @@ export default {
}
```
Use Node.js 26.4.0 with `--experimental-ffi`. If you enable the Node permission model, also pass `--permission`,
Use Node.js 26.4.0 or later with `--experimental-ffi`. If you enable the Node permission model, also pass `--permission`,
`--allow-ffi`, and the filesystem permissions that the application needs. The Solid Node source lane in CI compiles
TSX and runs on Linux x64.
@@ -77,7 +77,7 @@ A failed automatic start destroys the partial native engine. Successful automati
Audio uses the same native runtime as the renderer. Read [Runtime and platform support](/docs/getting-started/runtime-support) for setup and distribution details.
The local requirements are Bun 1.3.0 or later, or Node.js 26.4.0 with experimental FFI. Native packages support macOS, Linux, and Windows on x64 and arm64.
The local requirements are Bun 1.3.0 or later, or Node.js 26.4.0 or later with experimental FFI. Native packages support macOS, Linux, and Windows on x64 and arm64.
Linux selects glibc by default. Set the documented libc selector when the target needs musl. Audio has no browser target.
@@ -15,10 +15,9 @@ Check the runtime, native artifact, permission, asset, and package requirements
| Runtime | Project requirement | Native Core requirements |
| ------- | ------------------- | ------------------------------------------------------ |
| Bun | 1.3.0 or later | Bun loads the matching optional native package. |
| Node.js | 26.4.0 exactly | Use ECMAScript modules (ESM) and `--experimental-ffi`. |
| Node.js | 26.4.0 or later | Use ECMAScript modules (ESM) and `--experimental-ffi`. |
The Node.js acceptance scripts reject every version other than 26.4.0. Do not interpret this requirement as
Node.js 26 or later.
The Node.js acceptance scripts accept Node.js 26.4 or later.
Use ESM for Node.js applications. The Core root has an asynchronous ESM graph. A CommonJS `require("@opentui/core")`
call fails with `ERR_REQUIRE_ASYNC_MODULE`.
@@ -27,49 +27,49 @@ A row can have more than one class. Read [Runtime and platform support](/docs/ge
The runtime column describes the published target. A Node stub throws during import for each Bun-only support entry point.
| Package entry point | Runtime | Class | Canonical documentation |
| ------------------------------------------------- | --------------------------- | ----------------------------------------------- | ---------------------------------------------------------------- |
| `@opentui/core` | Bun and Node.js | Supported | [Renderer](/docs/core-concepts/renderer) |
| `@opentui/core/testing` | Bun and Node.js | Supported | [Testing](/docs/core-concepts/testing) |
| `@opentui/core/runtime-plugin` | Bun only | Runtime-specific, Advanced | [Runtime module loading](/docs/extend/runtime-plugins) |
| `@opentui/core/runtime-plugin-support` | Bun only | Runtime-specific, Advanced | [Runtime module loading](/docs/extend/runtime-plugins) |
| `@opentui/core/runtime-plugin-support/configure` | Bun only | Runtime-specific, Advanced | [Runtime module loading](/docs/extend/runtime-plugins) |
| `@opentui/core/yoga` | Bun and Node.js | Advanced | [Yoga API](/docs/reference/yoga) |
| `@opentui/core/tree-sitter/update-assets` | Build time, with Bun CLI | Runtime-specific | [Tree-sitter](/docs/reference/tree-sitter) |
| `@opentui/core/parser.worker` | Bun and Node.js worker | Implementation-looking public export | [Tree-sitter](/docs/reference/tree-sitter) |
| `@opentui/core/node-assets` | Node.js build time | Runtime-specific | [Standalone executables](/docs/reference/standalone-executables) |
| `@opentui/react` | Bun and Node.js | Supported | [React bindings](/docs/bindings/react) |
| `@opentui/react/renderer` | Bun and Node.js | Supported, Implementation-looking public export | [React bindings](/docs/bindings/react) |
| `@opentui/react/test-utils` | Bun and Node.js | Supported | [Testing](/docs/core-concepts/testing) |
| `@opentui/react/runtime-plugin-support` | Bun only | Runtime-specific, Advanced | [Runtime module loading](/docs/extend/runtime-plugins) |
| `@opentui/react/runtime-plugin-support/configure` | Bun only | Runtime-specific, Advanced | [Runtime module loading](/docs/extend/runtime-plugins) |
| `@opentui/react/jsx-runtime` | Compiler selected | Supported | [React bindings](/docs/bindings/react) |
| `@opentui/react/jsx-dev-runtime` | Compiler selected | Supported | [React bindings](/docs/bindings/react) |
| `@opentui/solid` | Bun and Node.js | Supported | [Solid bindings](/docs/bindings/solid) |
| `@opentui/solid/preload` | Bun only | Runtime-specific | [Solid bindings](/docs/bindings/solid) |
| `@opentui/solid/bun-plugin` | Bun only | Runtime-specific, Advanced | [Solid bindings](/docs/bindings/solid) |
| `@opentui/solid/runtime-plugin-support` | Bun only | Runtime-specific, Advanced | [Runtime module loading](/docs/extend/runtime-plugins) |
| `@opentui/solid/runtime-plugin-support/configure` | Bun only | Runtime-specific, Advanced | [Runtime module loading](/docs/extend/runtime-plugins) |
| `@opentui/solid/components` | Bun and Node.js | Advanced | [Solid bindings](/docs/bindings/solid) |
| `@opentui/solid/jsx-runtime` | Compiler selected | Supported | [Solid bindings](/docs/bindings/solid) |
| `@opentui/solid/jsx-dev-runtime` | Compiler selected | Supported | [Solid bindings](/docs/bindings/solid) |
| `@opentui/keymap` | JavaScript runtimes | Supported | [Core keymap API](/docs/keymap/core) |
| `@opentui/keymap/extras` | JavaScript runtimes | Supported | [Core keymap API](/docs/keymap/core) |
| `@opentui/keymap/extras/graph` | JavaScript runtimes | Advanced | [Core keymap API](/docs/keymap/core) |
| `@opentui/keymap/addons` | JavaScript runtimes | Supported | [Built-in keymap addons](/docs/keymap/addons) |
| `@opentui/keymap/addons/opentui` | Bun and Node.js | Supported | [Built-in keymap addons](/docs/keymap/addons) |
| `@opentui/keymap/testing` | JavaScript test runtimes | Supported | [Core keymap API](/docs/keymap/core) |
| `@opentui/keymap/html` | Browser DOM | Runtime-specific | [Keymap hosts](/docs/keymap/hosts) |
| `@opentui/keymap/opentui` | Bun and Node.js | Runtime-specific | [Keymap hosts](/docs/keymap/hosts) |
| `@opentui/keymap/react` | OpenTUI React | Runtime-specific | [React keymap integration](/docs/keymap/react) |
| `@opentui/keymap/solid` | OpenTUI Solid | Runtime-specific | [Solid keymap integration](/docs/keymap/solid) |
| `@opentui/keymap/runtime-modules` | Bun runtime loading | Runtime-specific, Advanced | [Runtime module loading](/docs/extend/runtime-plugins) |
| `@opentui/qrcode` | Bun and Node.js | Supported | [QR encoder](/docs/reference/qr-encoder) |
| `@opentui/qrcode/react` | OpenTUI React | Runtime-specific | [QR code component](/docs/components/qr-code) |
| `@opentui/qrcode/solid` | OpenTUI Solid | Runtime-specific | [QR code component](/docs/components/qr-code) |
| `@opentui/ssh` | Bun 1.3.0 or Node.js 26.4.0 | Runtime-specific | [SSH](/docs/reference/ssh) |
| `@opentui/three` | Bun only | Runtime-specific, Advanced | [Three.js WebGPU](/docs/reference/three) |
| `@opentui/three/runtime-modules` | Bun only | Runtime-specific, Advanced | [Runtime module loading](/docs/extend/runtime-plugins) |
| Package entry point | Runtime | Class | Canonical documentation |
| ------------------------------------------------- | ------------------------------------ | ----------------------------------------------- | ---------------------------------------------------------------- |
| `@opentui/core` | Bun and Node.js | Supported | [Renderer](/docs/core-concepts/renderer) |
| `@opentui/core/testing` | Bun and Node.js | Supported | [Testing](/docs/core-concepts/testing) |
| `@opentui/core/runtime-plugin` | Bun only | Runtime-specific, Advanced | [Runtime module loading](/docs/extend/runtime-plugins) |
| `@opentui/core/runtime-plugin-support` | Bun only | Runtime-specific, Advanced | [Runtime module loading](/docs/extend/runtime-plugins) |
| `@opentui/core/runtime-plugin-support/configure` | Bun only | Runtime-specific, Advanced | [Runtime module loading](/docs/extend/runtime-plugins) |
| `@opentui/core/yoga` | Bun and Node.js | Advanced | [Yoga API](/docs/reference/yoga) |
| `@opentui/core/tree-sitter/update-assets` | Build time, with Bun CLI | Runtime-specific | [Tree-sitter](/docs/reference/tree-sitter) |
| `@opentui/core/parser.worker` | Bun and Node.js worker | Implementation-looking public export | [Tree-sitter](/docs/reference/tree-sitter) |
| `@opentui/core/node-assets` | Node.js build time | Runtime-specific | [Standalone executables](/docs/reference/standalone-executables) |
| `@opentui/react` | Bun and Node.js | Supported | [React bindings](/docs/bindings/react) |
| `@opentui/react/renderer` | Bun and Node.js | Supported, Implementation-looking public export | [React bindings](/docs/bindings/react) |
| `@opentui/react/test-utils` | Bun and Node.js | Supported | [Testing](/docs/core-concepts/testing) |
| `@opentui/react/runtime-plugin-support` | Bun only | Runtime-specific, Advanced | [Runtime module loading](/docs/extend/runtime-plugins) |
| `@opentui/react/runtime-plugin-support/configure` | Bun only | Runtime-specific, Advanced | [Runtime module loading](/docs/extend/runtime-plugins) |
| `@opentui/react/jsx-runtime` | Compiler selected | Supported | [React bindings](/docs/bindings/react) |
| `@opentui/react/jsx-dev-runtime` | Compiler selected | Supported | [React bindings](/docs/bindings/react) |
| `@opentui/solid` | Bun and Node.js | Supported | [Solid bindings](/docs/bindings/solid) |
| `@opentui/solid/preload` | Bun only | Runtime-specific | [Solid bindings](/docs/bindings/solid) |
| `@opentui/solid/bun-plugin` | Bun only | Runtime-specific, Advanced | [Solid bindings](/docs/bindings/solid) |
| `@opentui/solid/runtime-plugin-support` | Bun only | Runtime-specific, Advanced | [Runtime module loading](/docs/extend/runtime-plugins) |
| `@opentui/solid/runtime-plugin-support/configure` | Bun only | Runtime-specific, Advanced | [Runtime module loading](/docs/extend/runtime-plugins) |
| `@opentui/solid/components` | Bun and Node.js | Advanced | [Solid bindings](/docs/bindings/solid) |
| `@opentui/solid/jsx-runtime` | Compiler selected | Supported | [Solid bindings](/docs/bindings/solid) |
| `@opentui/solid/jsx-dev-runtime` | Compiler selected | Supported | [Solid bindings](/docs/bindings/solid) |
| `@opentui/keymap` | JavaScript runtimes | Supported | [Core keymap API](/docs/keymap/core) |
| `@opentui/keymap/extras` | JavaScript runtimes | Supported | [Core keymap API](/docs/keymap/core) |
| `@opentui/keymap/extras/graph` | JavaScript runtimes | Advanced | [Core keymap API](/docs/keymap/core) |
| `@opentui/keymap/addons` | JavaScript runtimes | Supported | [Built-in keymap addons](/docs/keymap/addons) |
| `@opentui/keymap/addons/opentui` | Bun and Node.js | Supported | [Built-in keymap addons](/docs/keymap/addons) |
| `@opentui/keymap/testing` | JavaScript test runtimes | Supported | [Core keymap API](/docs/keymap/core) |
| `@opentui/keymap/html` | Browser DOM | Runtime-specific | [Keymap hosts](/docs/keymap/hosts) |
| `@opentui/keymap/opentui` | Bun and Node.js | Runtime-specific | [Keymap hosts](/docs/keymap/hosts) |
| `@opentui/keymap/react` | OpenTUI React | Runtime-specific | [React keymap integration](/docs/keymap/react) |
| `@opentui/keymap/solid` | OpenTUI Solid | Runtime-specific | [Solid keymap integration](/docs/keymap/solid) |
| `@opentui/keymap/runtime-modules` | Bun runtime loading | Runtime-specific, Advanced | [Runtime module loading](/docs/extend/runtime-plugins) |
| `@opentui/qrcode` | Bun and Node.js | Supported | [QR encoder](/docs/reference/qr-encoder) |
| `@opentui/qrcode/react` | OpenTUI React | Runtime-specific | [QR code component](/docs/components/qr-code) |
| `@opentui/qrcode/solid` | OpenTUI Solid | Runtime-specific | [QR code component](/docs/components/qr-code) |
| `@opentui/ssh` | Bun 1.3.0 or Node.js 26.4.0 or later | Runtime-specific | [SSH](/docs/reference/ssh) |
| `@opentui/three` | Bun only | Runtime-specific, Advanced | [Three.js WebGPU](/docs/reference/three) |
| `@opentui/three/runtime-modules` | Bun only | Runtime-specific, Advanced | [Runtime module loading](/docs/extend/runtime-plugins) |
See [Package entry points](/docs/reference/package-entrypoints) for each entry point's role and import constraints.
@@ -379,7 +379,7 @@ The package root works in Bun and Node.js. Each framework path re-exports the fu
## `@opentui/ssh`
The package supports Bun 1.3.0 and Node.js 26.4.0. It has one public entry point.
The package supports Bun 1.3.0 and Node.js 26.4.0 or later. It has one public entry point.
| Symbols | Class | Canonical documentation |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | -------------------------- |
@@ -95,9 +95,9 @@ The framework paths include the encoder and renderable exports. They are not reg
## `@opentui/ssh`
| Entry point | Runtime | Purpose | Workflow |
| -------------- | --------------------------- | ------------------------------------------------------------------------------------------- | -------------------------- |
| `@opentui/ssh` | Bun 1.3.0 or Node.js 26.4.0 | SSH server builder, authentication, middleware, sessions, logging, errors, and public types | [SSH](/docs/reference/ssh) |
| Entry point | Runtime | Purpose | Workflow |
| -------------- | ------------------------------------ | ------------------------------------------------------------------------------------------- | -------------------------- |
| `@opentui/ssh` | Bun 1.3.0 or Node.js 26.4.0 or later | SSH server builder, authentication, middleware, sessions, logging, errors, and public types | [SSH](/docs/reference/ssh) |
The package has no framework subpaths. Pass the session renderer to the React or Solid root API.
@@ -21,7 +21,7 @@ SSH is an access transport. It is not an application packaging format. See
bun add @opentui/ssh @opentui/core
```
`@opentui/core` is a peer dependency. The SSH package declares Bun >=1.3.0 and Node.js 26.4.0. The SSH Node CI lane
`@opentui/core` is a peer dependency. The SSH package declares Bun >=1.3.0 and Node.js >=26.4.0. The SSH Node CI lane
runs on Linux x64. Its packed Node consumer test uses a Core stub, so that test does not start the native renderer. See
[Runtime and platform support](/docs/getting-started/runtime-support) before you run the native renderer with Node.js.
@@ -56,7 +56,7 @@ apk add --no-cache libstdc++ libgcc
## Node.js SEA
Use exactly Node.js 26.4.0 for this procedure. The tested SEA is ESM and enables experimental FFI. It does not support
Use Node.js 26.4.0 or later for this procedure. The tested SEA is ESM and enables experimental FFI. It does not support
OpenTUI's Bun runtime-plugin system or `@opentui/three`.
Node stores SEA assets as bytes inside the executable. The OpenTUI native library and worker need filesystem paths.
@@ -144,7 +144,7 @@ function run(command, args) {
}
```
Run this build file with Node.js 26.4.0. The example builds for the host platform and architecture. It uses Bun only as
Run this build file with Node.js 26.4.0 or later. The example builds for the host platform and architecture. It uses Bun only as
the ESM bundler. The matching optional native package must be installed.
The application uses normal OpenTUI imports:
@@ -20,7 +20,7 @@ must reach the target machine.
| Bun source | You can install Bun and dependencies on the target. | Ship source, the lockfile, production dependencies, and the matching Core native package. |
| Bun bundle | You want built JavaScript but can install Bun on the target. | Ship the bundle and every emitted asset or external dependency. The build needs the matching native package unless it remains external. |
| Bun executable | You want one target-specific host executable. | Build once per operating system, architecture, and Linux libc. Deploy runtime-loaded plugins as sidecars. |
| Node.js ESM | You need the accepted Node.js runtime path without SEA packaging. | Use Node.js 26.4.0, ESM, `--experimental-ffi`, installed runtime assets, and the matching native package. |
| Node.js ESM | You need the accepted Node.js runtime path without SEA packaging. | Use Node.js 26.4.0 or later, ESM, `--experimental-ffi`, installed runtime assets, and the matching native package. |
| Node.js SEA | You need one Node.js executable and can extract its assets at startup. | Embed the exact Node asset manifest, extract it, and set `OTUI_ASSET_ROOT` before Core executes. |
| SSH | Users connect through an SSH client instead of a local terminal. | Package the host with one of the preceding forms, then configure listener security and remote-session policy. |
@@ -48,7 +48,7 @@ Use [Standalone executables](/docs/reference/standalone-executables#bun) for the
The accepted Node.js SEA workflow has these constraints:
- Use Node.js 26.4.0 and an ESM application bundle.
- Use Node.js 26.4.0 or later and an ESM application bundle.
- Do not use `@opentui/three` or runtime-plugin support. Those entry points are Bun-only.
- Call `getNodeAssets()` at build time only.
- Select the target platform, architecture, and Linux libc when you create the manifest.
@@ -22,7 +22,7 @@ Start with the visible symptom. Run the diagnostic in its row, then open the lin
| Symptom | Diagnostic and first action | Canonical guide |
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `OpenTUI native FFI is not available for this runtime yet` | Run `node --version`. It must print `v26.4.0`. Start the ESM app with `node --experimental-ffi app.mjs`. With `--permission`, also grant `--allow-ffi` and native-library read access. | [Node.js runtime](/docs/getting-started/runtime-support#runtime-versions) |
| `OpenTUI native FFI is not available for this runtime yet` | Run `node --version`. It must print `v26.4.0` or later. Start the ESM app with `node --experimental-ffi app.mjs`. With `--permission`, also grant `--allow-ffi` and native-library read access. | [Node.js runtime](/docs/getting-started/runtime-support#runtime-versions) |
| `ERR_REQUIRE_ASYNC_MODULE` | Find `require("@opentui/core")` or a CommonJS entry. Change the application to ESM and use `import`. | [Node.js runtime](/docs/getting-started/runtime-support#runtime-versions) |
| A native package or library is missing | Run `bun -e 'console.log(process.platform, process.arch, process.env.OPENTUI_LIBC ? process.env.OPENTUI_LIBC : "glibc")'`. Replace `bun` with `node` for a Node.js host. Check for the matching `@opentui/core-*` optional package. An install that omits optional dependencies can defer the failure until native work. | [Native artifacts](/docs/getting-started/runtime-support#native-artifacts) |
| Linux reports a loader error for `libopentui.so` | Compare the selected libc with the target system. Set `OPENTUI_LIBC=musl` before Core imports on musl. On Alpine, install `libstdc++` and `libgcc`. | [Select the Linux libc](/docs/getting-started/runtime-support#select-the-linux-libc) |
@@ -24,7 +24,7 @@ categories:
`@opentui/ssh` maps each accepted SSH shell to a `CliRenderer`. The renderer dimensions track the client's terminal.
The package requires Bun `>=1.3.0` or Node.js `26.4.0`.
The package requires Bun `>=1.3.0` or Node.js `>=26.4.0`.
Open authentication accepts SSH `none` without credentials. Use it only for loopback development. Do not expose open
authentication to a public or untrusted network.
+46 -3
View File
@@ -1,7 +1,7 @@
import { spawnSync } from "node:child_process"
import { delimiter, dirname } from "node:path"
export const NODE26_VERSION = "v26.4.0"
export const NODE26_MIN_VERSION = "v26.4.0"
export function requireNode26() {
const nodeCommand = typeof process.versions?.bun === "string" ? "node" : process.execPath
@@ -20,7 +20,7 @@ export function requireNode26() {
}
const runtime = parseNodeRuntime(nodeCommand, result.stdout)
if (runtime.version !== NODE26_VERSION) {
if (!isSupportedNode26Version(runtime.version)) {
throw new Error(nodeVersionError(`${runtime.execPath} reports ${runtime.version}`))
}
@@ -29,6 +29,49 @@ export function requireNode26() {
return runtime.execPath
}
export function isSupportedNode26Version(version) {
const parsed = parseNodeVersion(version)
const minimum = parseNodeVersion(NODE26_MIN_VERSION)
if (parsed === null || minimum === null) {
return false
}
return compareNodeVersion(parsed, minimum) >= 0
}
export function parseNodeVersion(version) {
if (typeof version !== "string") {
return null
}
const match = /^v?(\d+)\.(\d+)\.(\d+)/.exec(version)
if (match === null) {
return null
}
return {
major: Number(match[1]),
minor: Number(match[2]),
patch: Number(match[3]),
}
}
function compareNodeVersion(left, right) {
if (left.major !== right.major) {
return left.major < right.major ? -1 : 1
}
if (left.minor !== right.minor) {
return left.minor < right.minor ? -1 : 1
}
if (left.patch !== right.patch) {
return left.patch < right.patch ? -1 : 1
}
return 0
}
function parseNodeRuntime(nodeCommand, output) {
try {
const runtime = JSON.parse(output)
@@ -43,5 +86,5 @@ function parseNodeRuntime(nodeCommand, output) {
}
function nodeVersionError(actualVersion) {
return `Node.js ${NODE26_VERSION} is required, but ${actualVersion}. Select the required Node.js version before running this command; OpenTUI will not install it automatically.`
return `Node.js ${NODE26_MIN_VERSION} or later is required, but ${actualVersion}. Select a supported Node.js version before running this command; OpenTUI will not install it automatically.`
}