Skip to content

Commit f696518

Browse files
authored
Merge pull request #25 from hyfdev/agent/bun-deno-runtime-smoke
Add Bun and Deno runtime smoke tests
2 parents dbdad1e + 737fc72 commit f696518

12 files changed

Lines changed: 101 additions & 10 deletions

File tree

‎.agents/docs/intent.md‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,18 @@ TaffyJS is intended to bring Taffy, a high-performance layout engine written in
99
- A future `@taffyjs/yoga` package may provide a Yoga-compatible JavaScript API built on top of `@taffyjs/node` rather than as a second native implementation. [VOUCHED @hyfdev 2026-08-14]
1010
- The package family should support real consumers such as terminal UI frameworks without forcing those consumers to own their own Taffy bindings.
1111

12+
## Bun and Deno runtime support
13+
14+
[VOUCHED @hyfdev 2026-08-16]
15+
16+
**Ruling:** The implemented `@taffyjs/node` and `@taffyjs/wasm` packages support Bun 1.2+ within the Bun 1 major and Deno 2.2+ within the Deno 2 major. Runtime support follows one current major at a time rather than spanning majors.
17+
18+
**Limits:** Adopting a new Bun or Deno major requires choosing and documenting a new mainstream minimum minor and ends the support promise for the previous major. This ruling does not treat unimplemented packages as supported. Deno use of `@taffyjs/node` requires local `node_modules` plus environment, read, and FFI permissions; `@taffyjs/wasm` requires no Deno permission flags.
19+
20+
**Verification:** CI runs one minimal public-package layout smoke at only the first release of each supported minor floor, Bun 1.2.0 and Deno 2.2.0. It does not copy the complete behavior suite, test a second endpoint, or retain a cross-major matrix.
21+
22+
**Source:** Yunfei (`@hyfdev`), 2026-08-16; selected mainstream minimum minors within only the current runtime majors, chose Bun 1.2+ and Deno 2.2+ after both package paths passed at their `.0` releases, required only that minimum endpoint in CI, and asked to vouch the rule.
23+
1224
## @taffyjs/node binding role
1325

1426
`@taffyjs/node` exposes Taffy's high-level `TaffyTree` workflow without exposing low-level extension machinery or reimplementing layout in JavaScript. Taffy owns topology, Style, Layout, cache, and computation state; JavaScript adds only the public wrapper data needed for safe Node.js use.
@@ -19,5 +31,5 @@ The binding role and API priority are recorded in [@taffyjs/node decisions](taff
1931

2032
## Open questions
2133

22-
- Runtime and platform support beyond the current native targets and the approved `@taffyjs/wasm` Node.js and bundled-browser scope require a concrete consumer need.
34+
- Runtime and platform support beyond the current native targets, the approved `@taffyjs/wasm` Node.js and bundled-browser scope, Bun 1.2+ within major 1, and Deno 2.2+ within major 2 requires a concrete consumer need.
2335
- The coverage and release timing of `@taffyjs/yoga` remain open. Yoga compatibility is not part of the `@taffyjs/node` contract.

‎.agents/docs/taffyjs-wasm-package.md‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ The package reuses the public API and authored JavaScript behavior of `@taffyjs/
88

99
## Public use
1010

11-
Node.js and browser applications use the same ESM import and receive an initialized API:
11+
Node.js, Bun 1.2+ within major 1, Deno 2.2+ within major 2, and browser applications use the same ESM import and receive an initialized API:
1212

1313
```ts
1414
import { TaffyTree } from "@taffyjs/wasm";
@@ -44,7 +44,7 @@ Exact output extensions and directories may follow the repository's Vite+ conven
4444

4545
## Runtime chains
4646

47-
The default Node entry connects the existing authored TaffyJS source to a private ESM bridge and a generated CommonJS factory. The bridge statically imports the shared payload and immediately calls that factory. The factory is mechanically derived from napi-rs's generated eager Node loader: it decodes the supplied payload with `Buffer.from`, uses `instantiateNapiModuleSync`, and retains the generated registration, rollback, cleanup, and binding-export lifecycle. The selected graph has no top-level await, so ordinary static imports expose an initialized `TaffyTree` without making Node consumers await package initialization.
47+
The default entry used by Node.js, Bun, and Deno connects the existing authored TaffyJS source to a private ESM bridge and a generated CommonJS factory. The bridge statically imports the shared payload and immediately calls that factory. The factory is mechanically derived from napi-rs's generated eager Node loader: it decodes the supplied payload with `Buffer.from`, uses `instantiateNapiModuleSync`, and retains the generated registration, rollback, cleanup, and binding-export lifecycle. The selected graph has no top-level await, so ordinary static imports expose an initialized `TaffyTree` without making consumers await package initialization. Deno uses this package without runtime permission flags.
4848

4949
The browser entry connects the same authored source to a private ESM adapter. That adapter decodes the shared payload with `Uint8Array.fromBase64` when available and otherwise uses `atob` plus a direct `Uint8Array` fill, calls `WebAssembly.compile` exactly once, and passes the resulting `WebAssembly.Module` to napi-rs's unmodified deferred loader through `instantiate(module)`. It uses top-level await, so browser static importers run only after initialization and still use `new TaffyTree()` without an explicit initialization call.
5050

@@ -68,11 +68,12 @@ Both runtime chains use napi-rs's JavaScript WASI implementation without forward
6868
- Do not publish or ask users to install a TaffyJS WASIP binding package.
6969
- Do not add a public initialization API, public raw binding, hand-maintained WASI or emnapi implementation, or Wasm-specific Taffy API.
7070
- Do not use wasm-bindgen, a second Rust binding implementation, a second authored `TaffyTree`, gzip, `wasm-opt`, or a threaded WASIP target.
71-
- Do not claim workerd, Deno, Bun, unbundled CDN, or legacy-browser support without separate evidence and a deliberate support decision.
71+
- Do not claim support below the documented Bun or Deno minor floors, across Bun or Deno majors, for workerd, for unbundled CDN use, or for legacy browsers without separate evidence and a deliberate support decision.
7272

7373
## Verification contract
7474

7575
- A Node.js ESM consumer must import `@taffyjs/wasm` and use the ordinary public Taffy API without initialization calls, top-level await in the selected package graph, or native platform packages.
76+
- Consumers on Bun 1.2+ within major 1 and Deno 2.2+ within major 2 must import the same public entry and complete a minimal layout through the ordinary API; the Deno check must run without permission flags. CI covers only Bun 1.2.0 and Deno 2.2.0 as the first releases of the supported minor floors and does not retain a cross-major runtime matrix.
7677
- A bundled browser consumer must use the same import and API, include the inline payload without emitting a `.wasm` asset, and work without `SharedArrayBuffer`, cross-origin isolation, COOP, or COEP.
7778
- The Node and browser builds must derive their JavaScript and TypeScript surface from the same authored source. Except for a documented and evidenced host-specific exception, every public Node binding behavior test must run unchanged against both `@taffyjs/node` and `@taffyjs/wasm`; one test configuration redirects the exact package import instead of copying test files.
7879
- Package inspection must show exactly one base64 Wasm payload, no independent `.wasm` file, exactly one browser-side `WebAssembly.compile`, synchronous `instantiateNapiModuleSync` in the Node graph, no Node environment or filesystem-root WASI capabilities, no retained `require.resolve` or dependency on `@taffyjs/binding-wasm32-wasip1`, no public initialization subpath, and no accidentally published raw binding entry.

‎.agents/docs/technology-stack.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ The root Vite+ task graph builds the napi-rs artifacts into `packages/taffyjs-wa
2626

2727
`tools/api-codegen` owns source generation that must keep Rust and TypeScript API facts aligned. Its first maintained input is `api/numeric-families.json`; `vp run codegen` updates both language outputs. CI runs `vp run check:codegen`, which regenerates and rejects any resulting Git diff.
2828

29-
CI has five jobs. Ubuntu x64 and Windows x64 each build the native addon and run all Rust, JavaScript, and type tests with Node.js 22.18.0. Ubuntu also rejects stale committed package JavaScript and declarations after the build. A separate Ubuntu WASIP job installs the Rust target and Playwright Chromium, builds `@taffyjs/wasm`, reruns the complete Node public API suite against it, and runs type, package-content, packed-consumer, bundled-consumer, and browser-runtime checks against the generated package. A Node-only job checks formatting, JavaScript and repository TypeScript including maintained tools through Vite+'s type-aware lint path, and generated-source drift. A Rust-only job checks formatting and Clippy. macOS and publication workflows are not configured.
29+
CI has five jobs. Ubuntu x64 and Windows x64 each build the native addon and run all Rust, JavaScript, and type tests with Node.js 22.18.0, then smoke-test the public `@taffyjs/node` entry with Bun 1.2.0 and Deno 2.2.0; Ubuntu also rejects stale committed package JavaScript and declarations after the build. A separate Ubuntu WASIP job installs the Rust target and Playwright Chromium, builds `@taffyjs/wasm`, reruns the complete Node public API suite against it, runs type, package-content, packed-consumer, bundled-consumer, and browser-runtime checks against the generated package, and applies the same Bun 1.2.0 and Deno 2.2.0 smoke checks to the public entry. Alternate-runtime smoke checks perform one fixed layout rather than copying the complete behavior suite. A Node-only job checks formatting, JavaScript and repository TypeScript including maintained tools through Vite+'s type-aware lint path, and generated-source drift. A Rust-only job checks formatting and Clippy. macOS and publication workflows are not configured.
3030

3131
## Public website
3232

‎.github/workflows/ci.yml‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ jobs:
2424
- run: pnpm install --frozen-lockfile
2525
- name: Build and run all tests
2626
run: pnpm exec vp run check:test
27+
- name: Smoke test @taffyjs/node with Bun 1.2
28+
run: pnpm dlx bun@1.2.0 packages/taffyjs-node/tests/runtime-smoke.mjs
29+
- name: Smoke test @taffyjs/node with Deno 2.2
30+
run: pnpm dlx deno@2.2.0 run --node-modules-dir=manual --allow-env --allow-read --allow-ffi packages/taffyjs-node/tests/runtime-smoke.mjs
2731
- name: Verify committed package outputs are current
2832
run: git diff --exit-code -- packages/taffyjs-node/index.js packages/taffyjs-node/index.d.ts packages/taffyjs-node/binding.js packages/taffyjs-node/binding.d.ts
2933

@@ -42,6 +46,10 @@ jobs:
4246
- run: pnpm install --frozen-lockfile
4347
- name: Build and run all tests
4448
run: pnpm exec vp run check:test
49+
- name: Smoke test @taffyjs/node with Bun 1.2
50+
run: pnpm dlx bun@1.2.0 packages/taffyjs-node/tests/runtime-smoke.mjs
51+
- name: Smoke test @taffyjs/node with Deno 2.2
52+
run: pnpm dlx deno@2.2.0 run --node-modules-dir=manual --allow-env --allow-read --allow-ffi packages/taffyjs-node/tests/runtime-smoke.mjs
4553

4654
test-wasm:
4755
name: Build and test WASIP
@@ -62,6 +70,10 @@ jobs:
6270
run: pnpm --filter tests-taffy-wasm exec playwright install --with-deps chromium
6371
- name: Build and test the Wasm package
6472
run: pnpm exec vp run check:wasm
73+
- name: Smoke test @taffyjs/wasm with Bun 1.2
74+
run: pnpm dlx bun@1.2.0 packages/taffyjs-wasm/tests/runtime-smoke.mjs
75+
- name: Smoke test @taffyjs/wasm with Deno 2.2
76+
run: pnpm dlx deno@2.2.0 run --node-modules-dir=manual packages/taffyjs-wasm/tests/runtime-smoke.mjs
6577

6678
check-node:
6779
name: Node checks

‎README.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,17 @@ npm install @taffyjs/node
2626
| [`@taffyjs/yoga`](https://www.npmjs.com/package/@taffyjs/yoga) | Yoga-compatible API backed by `@taffyjs/node`. | — |
2727
| [`@taffyjs/yoga-wasm`](https://www.npmjs.com/package/@taffyjs/yoga-wasm) | Yoga-compatible API backed by `@taffyjs/wasm`. | WIP |
2828

29+
## Runtime compatibility
30+
31+
TaffyJS supports Bun 1.2+ within the Bun 1 major and Deno 2.2+ within the Deno 2 major. CI smoke-tests the first release of each minimum minor by importing each public package, computing a minimal layout, and verifying the result. Support follows one current major per runtime rather than spanning runtime majors.
32+
33+
| Package | Bun 1.2+ | Deno 2.2+ |
34+
| -------------------- | ------------------ | --------------------------------------------------------------------------- |
35+
| `@taffyjs/node` | ✅ | ✅ (local `node_modules`; `--allow-env`, `--allow-read`, and `--allow-ffi`) |
36+
| `@taffyjs/wasm` | ✅ | ✅ (without permission flags) |
37+
| `@taffyjs/yoga` | ⚠️ Not implemented | ⚠️ Not implemented |
38+
| `@taffyjs/yoga-wasm` | ⚠️ Not implemented | ⚠️ Not implemented |
39+
2940
## Documentation
3041

3142
- [Guide](apps/website/guide/index.md) explains the shared layout model, layout modes, and text and image measurement.

‎apps/website/node/index.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ The package requires Node.js 22.18.0 or newer and currently provides native bina
1313
| Linux | x64 | `x86_64-unknown-linux-gnu` |
1414
| Windows | x64 | `x86_64-pc-windows-msvc` |
1515

16+
Bun 1.2+ within major 1 and Deno 2.2+ within major 2 are also supported on these native targets. CI smoke-tests Bun 1.2.0 and Deno 2.2.0 through the public package entry. Support does not span runtime majors. Deno requires a local `node_modules` directory and the `--allow-env`, `--allow-read`, and `--allow-ffi` permissions.
17+
1618
The package is ESM-only. Application code imports the public package entry:
1719

1820
```ts

‎apps/website/wasm/index.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@ The [Guide](../guide/getting-started.md) and [`TaffyTree` reference](../node/nod
3131
| Environment | Support |
3232
| ----------- | --------------------------------------------------------------------------------------------- |
3333
| Node.js | Node.js 22.18 or newer |
34+
| Bun | Version 1.2 or newer within the Bun 1 major |
35+
| Deno | Version 2.2 or newer within the Deno 2 major, without permission flags |
3436
| Browser | Applications built with a bundler that supports package export conditions and top-level await |
3537

3638
The browser build contains the Wasm payload inside JavaScript, so it does not fetch a separate `.wasm` file. It is threadless and does not require `SharedArrayBuffer` or cross-origin isolation headers.
3739

38-
Direct imports from a CDN and runtimes other than the supported Node.js and bundled-browser paths are not currently part of the package contract.
40+
Bun versions below 1.2, Deno versions below 2.2, other Bun and Deno majors, direct CDN imports, and runtimes not listed above are not part of the package contract.
3941

4042
## Loading
4143

@@ -58,7 +60,7 @@ Your bundler must select the package's browser entry. No TaffyJS-specific loader
5860
| | `@taffyjs/node` | `@taffyjs/wasm` |
5961
| ------------------------------- | -------------------------------- | ---------------------------------------- |
6062
| Engine | Taffy compiled as a native addon | Taffy compiled to threadless WebAssembly |
61-
| Main use | Supported Node.js platforms | Bundled browsers and supported Node.js |
63+
| Main use | Supported native platforms | Bundled browsers and supported runtimes |
6264
| Public layout API | Direct TaffyJS API | The same direct TaffyJS API |
6365
| Initialization visible to users | None | None |
6466
| Distribution | Platform-specific native binary | Wasm payload embedded in JavaScript |

‎packages/taffyjs-node/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Install it with npm:
88
npm install @taffyjs/node
99
```
1010

11-
The package requires Node.js 22.18.0 or newer. Its native targets are Linux x64 GNU and Windows x64 MSVC. Application code imports only the public package entry:
11+
The package requires Node.js 22.18.0 or newer. Its native targets are Linux x64 GNU and Windows x64 MSVC. Bun 1.2+ within major 1 and Deno 2.2+ within major 2 are also supported and smoke-tested on those targets; support does not span runtime majors. Deno requires a local `node_modules` directory and the `--allow-env`, `--allow-read`, and `--allow-ffi` permissions. Application code imports only the public package entry:
1212

1313
```ts
1414
import { TaffyTree } from "@taffyjs/node";
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { TaffyTree } from "@taffyjs/node";
2+
3+
import { runRuntimeSmoke } from "../../../tests/runtime-smoke.ts";
4+
5+
const packageName = "@taffyjs/node";
6+
const resolvedEntry = import.meta.resolve(packageName);
7+
8+
if (!resolvedEntry.endsWith("/index.js")) {
9+
throw new Error(`${packageName} resolved to an unexpected entry: ${resolvedEntry}`);
10+
}
11+
12+
runRuntimeSmoke(packageName, new TaffyTree());

‎packages/taffyjs-wasm/README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# `@taffyjs/wasm`
22

3-
`@taffyjs/wasm` runs the Taffy layout engine as threadless WebAssembly in Node.js and bundled browsers. It exposes the same public TaffyJS API as `@taffyjs/node`; choosing this package guarantees that the implementation is Wasm rather than a native addon.
3+
`@taffyjs/wasm` runs the Taffy layout engine as threadless WebAssembly in Node.js, Bun 1.2+ within major 1, Deno 2.2+ within major 2, and bundled browsers. It exposes the same public TaffyJS API as `@taffyjs/node`; choosing this package guarantees that the implementation is Wasm rather than a native addon.
44

55
```ts
66
import { TaffyTree } from "@taffyjs/wasm";
@@ -16,4 +16,4 @@ No explicit initialization call is needed. The Wasm payload is inlined once in t
1616
const { TaffyTree } = await import("@taffyjs/wasm");
1717
```
1818

19-
The supported browser path is through a bundler that honors conditional exports and top-level await. It does not emit or fetch a separate `.wasm` asset. Direct CDN use and runtimes other than supported Node.js and bundled browsers are not currently part of the package contract.
19+
The supported browser path is through a bundler that honors conditional exports and top-level await. It does not emit or fetch a separate `.wasm` asset. Deno needs no runtime permission flags. Bun versions below 1.2, Deno versions below 2.2, other Bun and Deno majors, direct CDN use, and other runtimes are not currently part of the package contract.

0 commit comments

Comments
 (0)