Skip to content

Add optional C Node-API backend with Wasm fallback - #160

Open
taras wants to merge 3 commits into
bombshell-dev:mainfrom
taras:native-node-api
Open

taras wants to merge 3 commits into
bombshell-dev:mainfrom
taras:native-node-api

Conversation

@taras

@taras taras commented Oct 22, 2026

Copy link
Copy Markdown

What does this PR do?

Add a C Node-API backend so Node.js can run the existing engine as a native
.node addon without a Rust rewrite. A matching packaged addon is selected
automatically; when none is packaged, the library uses Wasm. The public
TypeScript API is unchanged. No separate issue is needed for this self-contained
backend experiment.

  • Support native rendering, input parsing, and terminfo detection without
    preparing Wasm on the native path.
  • Replace wasm32 pointer assumptions with typed pointers and C text measurement.
    Keep layout contexts on each Term, route callbacks through Clay's userData,
    and isolate upstream routing state and overflow sentinels per worker.
  • Preserve buffer ownership across resize and collection, and reject native
    calls after a backing buffer is detached.
  • Add local macOS/Linux builds, npm artifact copying, native worker/lifetime
    checks, and a reproducible comparison with the existing rendering workloads.
    Apply Clay patches to a generated copy instead of resetting the submodule.

Benchmark results

Apple M1 Max, macOS ARM64, Node.js 26.5.1. Both C targets use Clang 20.1.7 with
-O2. These are local measurements using identical TypeScript sources and the
public APIs.

Workload Wasm Native Native result
Mixed rendering 33.40 µs/frame 26.34 µs/frame 27% higher throughput
Steady diff 30.21 µs/frame 26.48 µs/frame 14% higher throughput
Parse 48 KB mixed input 1.671 ms 1.681 ms Essentially tied
Import + createTerm 25.95 ms 22.73 ms 12% less time
Import + createInput 26.14 ms 22.61 ms 13% less time
Import + first render 28.48 ms 22.84 ms 20% less time

Whole-process first-render startup was essentially unchanged: 100.54 ms for
Wasm and 100.24 ms for native in this harness. The startup figures above include
module import and TypeScript loading; they are not measurements of the factory
call alone or of a transpiled npm package.

Throughput uses three alternating processes per backend, with 300 ms warmup
and seven 150 ms samples per workload per process. Startup uses 20 fresh
processes per backend after three warmups. Results are medians.

Raw results and compiler flags.
Reproduce with make, make native, then node bench/native.bench.ts.

Validation and scope

  • Wasm: 29 tests with 385 steps passed.
  • Native Node.js: 339 tests passed through the existing suite.
  • Five native renderer checks passed, covering multiple Terms, four concurrent
    workers, output lifetime across resize/GC, detached buffers, and absence of
    Wasm preparation.
  • deno task fmt, deno task fmt:check, deno lint, TypeScript checks, and
    git diff --check passed.

Native execution was validated locally on macOS ARM64. Cross-platform prebuilt
binary distribution and native CI coverage are not automated by this PR; the
local build supports macOS/Linux, and other platforms retain Wasm fallback.

Type of change

  • Bug fix
  • Feature
  • Refactor (no behavior change)
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • All tests pass (deno task test, native Node suite, native renderer checks)
  • Files are formatted (deno task fmt)
  • I have added/updated tests for my changes
  • I have added a changeset

AI-generated code disclosure

  • This PR includes AI-generated code

@bombshell-cooper

Copy link
Copy Markdown
Contributor

Changeset suggestion

The current changeset no longer matches this PR. This review contains a corrected replacement.

Why: Changeset should start with an approved active present-tense verb. Changeset does not name the affected public API confidently. Changeset is not sufficiently consumer-focused.

View the proposed changeset
---
'@bomb.sh/tty': minor
---

TODO: Write changeset message (inference failed: HTTP 401: {"error":{"message":"User not found.","code":401}})

Review this changeset manually

If this draft is correct, react with 🚀 and Cooper will commit it to this branch.

@@ -0,0 +1,7 @@
---

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changeset needs revision.

Changeset should start with an approved active present-tense verb. Changeset does not name the affected public API confidently. Changeset is not sufficiently consumer-focused.

View the proposed replacement
---
'@bomb.sh/tty': minor
---

TODO: Write changeset message (inference failed: HTTP 401: {"error":{"message":"User not found.","code":401}})

@pkg-pr-new

pkg-pr-new Bot commented Oct 22, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@bomb.sh/tty@160

commit: c2bbd7e

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant