## Summary
- use `buffer` ABI parameters and pass typed-array owners directly for
transient synchronous Bun FFI calls
- lower portable `buffer` parameters to Node's stable `pointer` path
while still passing owner objects directly
- keep `ptr` only for nullable, mixed native-pointer, raw `ArrayBuffer`,
callback, and retained-memory cases
- stabilize retained text-memory views through `.buffer` before
resolving their native address
- preserve raw-pointer compatibility while allowing direct buffers in
supersample, packed-buffer, matrix, and grayscale APIs
- document the Bun 1.3.14 and Bun 1.4+ ownership rules in `AGENTS.md`
## Why
A Bun `FastTypedArray` may store its data inline. Calling `ptr(view)`
captures that address, but a later first access to `view.buffer` can
move the data into separate `ArrayBuffer` storage and leave the native
address stale. Passing the owner directly lets the FFI backend borrow
the current storage for synchronous calls and allows Bun's `buffer` fast
path to keep the owner visible to the JIT.
This keeps nullable and true native-pointer parameters on `ptr`, where
`buffer` cannot represent the ABI, while avoiding pre-resolved addresses
for transient memory. Retained pointers explicitly materialize stable
backing storage before `ptr(view)` and keep the view alive for the
native lifetime.
Node 26.4's Linux optimized `buffer` trampoline delivered a null pointer
to a multi-argument audio call in CI. OpenTUI therefore maps its
portable `buffer` descriptor to Node `pointer`, whose documented
owner-borrowing path passes the same views correctly. Bun continues to
receive the `buffer` descriptor.
Related Bun investigation: oven-sh/bun#32054 and oven-sh/bun#32055.
## Testing
- `bunx bun@1.3.14 test
src/tests/ffi-borrowed-pointer-callsites.test.ts` (23 passed)
- `bun test src/tests/ffi-borrowed-pointer-callsites.test.ts` on Bun 1.4
canary (23 passed)
- `bun run test:js` (5,397 passed, 23 skipped)
- `bun run test:js:node` with Node 26.4.0 (4,665 passed, 6 skipped)
- `bun run test:dist`
- `bun run build:lib`
- `bun run fmt:check`
- `bun run lint`
I tried rewriting the `AGENTS.md` around the workflows and failure modes
that recur in
current OpenTUI development.
I had opencode look at:
- The most recent 50 commits, especially runtime portability, native
lifecycle,
renderer behavior, terminal width, and FFI changes.
- My OpenCode sessions to identify recurring agent mistakes and
unnecessary
abstractions.
- Current package scripts, CI workflows, development documentation, and
runtime
implementations.
The previous guide mixed project-specific constraints with generic
TypeScript advice. I think many of these were stale.
The replacement keeps the guide to that cannot be inferred from nearby
code: ownership across TypeScript and Zig, display-cell width semantics,
package-specific verification, Bun and Node portability, terminal-driven
debugging, and borrowed FFI pointer lifetimes.
If this turns out to not work lets revert to the previous one and
iterate in
smaller steps.
Pass transient buffers to FFI calls as borrowed objects so Node keeps
the JavaScript owner alive for the synchronous
native read. Raw addresses can outlive the object that backs them, which
lets GC corrupt styled text and cursor option
data
Fixes#1212