diff --git a/.agents/docs/architecture.md b/.agents/docs/architecture.md index d234333..1b4cf6f 100644 --- a/.agents/docs/architecture.md +++ b/.agents/docs/architecture.md @@ -8,7 +8,7 @@ This repository is a small Rust and JavaScript monorepo with one shared napi-rs - `crates/taffyjs_binding` owns the napi-rs adapter and depends directly on Taffy. A shared Rust crate is justified only when a second Rust consumer needs it. - `packages/taffyjs-node` owns the public ESM wrapper and declarations, the private napi-rs loader and declarations, and npm metadata. The wrapper owns JavaScript-only NodeId validity data and context; Taffy owns topology, Style, Layout, cache, and computation state. - `packages/taffyjs-wasm` compiles the same authored source from `packages/taffyjs-node/src` against generated private Node and browser adapters that share one inline payload. It owns package metadata, generated artifacts, and build-time binding redirection; its package-specific typed generator lives under `tools/taffy-wasm`. It has no authored public wrapper source or separate initialization API. -- `packages/taffyjs-yoga` owns the Node-only ESM Yoga 3.2.1 compatibility facade, Yoga declarations and visible state, input translation, and Yoga-shaped output projection. It depends only on the public `@taffyjs/node` package boundary and does not modify Taffy or the native binding. +- `packages/taffyjs-yoga` owns the native ESM Yoga 3.2.1 compatibility facade, Yoga declarations and visible state, input translation, and Yoga-shaped output projection. It depends only on the public `@taffyjs/node` package boundary and does not modify Taffy or the native binding. - `packages/taffyjs-yoga-wasm` builds the exact facade source owned by `packages/taffyjs-yoga/src` against the public `@taffyjs/wasm` backend. It owns only its package metadata and build-time backend redirection; it has no copied facade source or second compatibility policy. - `benchmarks` owns the Node-only end-to-end public JavaScript benchmark cases, their TypeScript coordinator, ignored local results, and the retained dataset consumed by the public website. Each scenario owns a direct child directory; the complete boundary is recorded in [Benchmark Design](benchmark.md). - `tests/taffyjs-node` is a private consumer package that tests `@taffyjs/node` through its package boundary. @@ -18,7 +18,7 @@ This repository is a small Rust and JavaScript monorepo with one shared napi-rs The `@taffyjs/node` public entry bundles the private napi-rs root loader, which selects the matching optional platform package. Its generated loader and declaration remain repository build inputs rather than published package files. The `@taffyjs/wasm` default entry reaches a no-TLA ESM bridge that imports the shared payload and immediately invokes a synchronous CommonJS factory mechanically derived from napi-rs's eager Node loader; its browser entry reaches a TLA adapter and the unmodified deferred loader. Neither direct runtime package exposes an intermediate binding package, JavaScript shadow tree, separate core crate, public binding, or public initialization API. -`@taffyjs/yoga` is a Node-only TypeScript compatibility layer over `@taffyjs/node`; browser and WASM backends never live in that package. It performs bounded input normalization and output processing, but it does not modify or fork Taffy core, add a Yoga-specific native algorithm, or reimplement Flex layout in JavaScript; behavior that cannot cross that boundary is documented as Different or Unsupported as recorded in [@taffyjs/yoga decisions](taffyjs-yoga-decisions.md). +`@taffyjs/yoga` is a native TypeScript compatibility layer over `@taffyjs/node` for Node.js, Bun, and Deno; browser and WASM backends never live in that package. It performs bounded input normalization and output processing, but it does not modify or fork Taffy core, add a Yoga-specific native algorithm, or reimplement Flex layout in JavaScript; behavior that cannot cross that boundary is documented as Different or Unsupported as recorded in [@taffyjs/yoga decisions](taffyjs-yoga-decisions.md). `@taffyjs/yoga-wasm` is a separate transport over `@taffyjs/wasm`, selected at build time by replacing only the exact bare `@taffyjs/node` dependency. The eager root entry and lazy `/load` entry keep Yoga's public shapes; `/load` dynamically imports the facade so a browser compiles no WebAssembly until `loadYoga()` is called. Compatibility semantics stay owned by the shared Yoga source and documentation. diff --git a/.agents/docs/intent.md b/.agents/docs/intent.md index 18cc157..0580211 100644 --- a/.agents/docs/intent.md +++ b/.agents/docs/intent.md @@ -6,8 +6,8 @@ TaffyJS is intended to bring Taffy, a high-performance layout engine written in - `@taffyjs/node` is the first entry point and provides native Node-API bindings built with napi-rs. - `@taffyjs/wasm` is the explicit WebAssembly entry point for Node.js and bundled browsers. It reuses the `@taffyjs/node` public API over a threadless napi-rs WASIP binding rather than acting as an automatic fallback; its vouched contract is in [@taffyjs/wasm package design](taffyjs-wasm-package.md). -- `@taffyjs/yoga` is the Node-only Yoga 3.2.1 compatibility package built on top of `@taffyjs/node`; browser and WASM backends remain outside that package. -- `@taffyjs/yoga-wasm` is the separate Node.js and bundled-browser transport for that same compatibility facade. It builds the same source over `@taffyjs/wasm` so the two packages cannot drift into separate Yoga implementations. +- `@taffyjs/yoga` is the native Yoga 3.2.1 compatibility package built on top of `@taffyjs/node` for Node.js, Bun, and Deno; browser and WASM backends remain outside that package. +- `@taffyjs/yoga-wasm` is the separate Node.js, Bun, Deno, and bundled-browser transport for that same compatibility facade. It builds the same source over `@taffyjs/wasm` so the two packages cannot drift into separate Yoga implementations. - The package family should support real consumers such as terminal UI frameworks without forcing those consumers to own their own Taffy bindings. ## Bun and Deno runtime support diff --git a/.agents/docs/taffyjs-yoga-wasm-package.md b/.agents/docs/taffyjs-yoga-wasm-package.md index 7080e4a..6559665 100644 --- a/.agents/docs/taffyjs-yoga-wasm-package.md +++ b/.agents/docs/taffyjs-yoga-wasm-package.md @@ -5,7 +5,7 @@ ## Public contract - The package is ESM-only and exposes the same root and `/load` shapes, declarations, constants, and Yoga compatibility classifications as `@taffyjs/yoga`. -- The package supports Node.js `>=22.18` and modern bundled browsers within the existing `@taffyjs/wasm` contract. It does not claim direct CDN, legacy-browser, Bun, Deno, workerd, or other edge-runtime support without separate evidence. +- The package supports Node.js `>=22.18`, Bun 1.2+ within major 1, Deno 2.2+ within major 2, and modern bundled browsers within the existing `@taffyjs/wasm` contract. Deno requires no permission flags. It does not claim direct CDN, legacy-browser, workerd, or other edge-runtime support without separate evidence. - Consumers may install it under Yoga's original name with the registry alias `"yoga-layout": "npm:@taffyjs/yoga-wasm@"`; supported application imports remain unchanged. - `PositionType.Static` and every other Unsupported capability stay unsupported. Replacing the runtime transport does not create missing Taffy layout semantics. @@ -36,5 +36,6 @@ Expected validation, callback, and Taffy errors retain the compatibility facade' - Pack both `@taffyjs/wasm` and `@taffyjs/yoga-wasm`; install the latter under the bare `yoga-layout` dependency name in fresh npm and pnpm consumers; run both Node entries. - From the installed tarballs, build bare `yoga-layout` and `yoga-layout/load` imports with Vite. Each browser bundle must select the `@taffyjs/wasm` browser condition, contain one inline payload and one `WebAssembly.compile`, emit no `.wasm` asset, and contain no `node:wasi` path. - Run both installed browser entries in real Chromium without cross-origin isolation or `SharedArrayBuffer`. Importing `/load` must compile zero Wasm modules; the first `loadYoga()` call must compile exactly one. +- Run one fixed-layout smoke through both public entries on Bun 1.2.0 and Deno 2.2.0, the first releases of the supported minimum minors. The Deno smoke uses no permission flags, matching the underlying `@taffyjs/wasm` runtime. -The Yoga Wasm checks live in the existing Linux-only `check:wasm` graph because they require the WASIP Rust target and Chromium. Native cross-platform checks continue to own `@taffyjs/yoga`. +The Yoga Wasm build, package, Node.js, and browser checks live in the existing Linux-only `check:wasm` graph because they require the WASIP Rust target and Chromium. CI runs the alternate-runtime smokes immediately afterward using the packages produced by that graph. Native cross-platform checks continue to own `@taffyjs/yoga`. diff --git a/.agents/docs/technology-stack.md b/.agents/docs/technology-stack.md index 83ea8e5..5c9c5da 100644 --- a/.agents/docs/technology-stack.md +++ b/.agents/docs/technology-stack.md @@ -32,7 +32,7 @@ For `packages/taffyjs-yoga-wasm`, Vite+ builds those same sibling source entries `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. -CI has six jobs. Ubuntu x64, Windows x64, and macOS arm64 each build the native addon and run all Rust, JavaScript, and type tests with Node.js 22.18.0; Ubuntu and Windows then smoke-test the public `@taffyjs/node` and `@taffyjs/yoga` entries 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. That job also builds `@taffyjs/yoga-wasm`, reruns the complete maintained Yoga behavior and declaration suites, inspects and installs the packed package with npm and pnpm, and exercises both public entries in bundled Chromium. 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. Publication workflows are not configured. +CI has six jobs. Ubuntu x64, Windows x64, and macOS arm64 each build the native addon and run all Rust, JavaScript, and type tests with Node.js 22.18.0; Ubuntu and Windows then smoke-test the public `@taffyjs/node` and `@taffyjs/yoga` entries 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. That job also builds `@taffyjs/yoga-wasm`, reruns the complete maintained Yoga behavior and declaration suites, inspects and installs the packed package with npm and pnpm, exercises both public entries in bundled Chromium, and smoke-tests both entries with Bun 1.2.0 and Deno 2.2.0. 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. Publication workflows are not configured. ## Public website diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33225ac..07b80f2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -98,6 +98,10 @@ jobs: run: pnpm dlx bun@1.2.0 packages/taffyjs-wasm/tests/runtime-smoke.mjs - name: Smoke test @taffyjs/wasm with Deno 2.2 run: pnpm dlx deno@2.2.0 run --node-modules-dir=manual packages/taffyjs-wasm/tests/runtime-smoke.mjs + - name: Smoke test @taffyjs/yoga-wasm with Bun 1.2 + run: pnpm dlx bun@1.2.0 tests/taffyjs-yoga-wasm/runtime-smoke.mjs + - name: Smoke test @taffyjs/yoga-wasm with Deno 2.2 + run: pnpm dlx deno@2.2.0 run --node-modules-dir=manual tests/taffyjs-yoga-wasm/runtime-smoke.mjs check-node: name: Node checks diff --git a/README.md b/README.md index f9f2a2b..8e3939a 100644 --- a/README.md +++ b/README.md @@ -30,12 +30,12 @@ npm install @taffyjs/node TaffyJS supports Node.js 22.18+, Bun 1.2+ within the Bun 1 major, and Deno 2.2+ within the Deno 2 major. CI runs the complete test suite on Node.js 22.18.0 and smoke-tests Bun 1.2.0 and Deno 2.2.0 by importing each implemented public package, computing a minimal layout, and verifying the result. Bun and Deno support follows one current major per runtime rather than spanning runtime majors. -| Package | Node.js 22.18+ | Bun 1.2+ | Deno 2.2+ | -| -------------------- | ------------------ | ------------------ | --------------------------------------------------------------------------- | -| `@taffyjs/node` | ✅ | ✅ | ✅ (local `node_modules`; `--allow-env`, `--allow-read`, and `--allow-ffi`) | -| `@taffyjs/wasm` | ✅ | ✅ | ✅ (without permission flags) | -| `@taffyjs/yoga` | ✅ | ✅ | ✅ (local `node_modules`; `--allow-env`, `--allow-read`, and `--allow-ffi`) | -| `@taffyjs/yoga-wasm` | ⚠️ Not implemented | ⚠️ Not implemented | ⚠️ Not implemented | +| Package | Node.js 22.18+ | Bun 1.2+ | Deno 2.2+ | +| -------------------- | -------------- | -------- | --------------------------------------------------------------------------- | +| `@taffyjs/node` | ✅ | ✅ | ✅ (local `node_modules`; `--allow-env`, `--allow-read`, and `--allow-ffi`) | +| `@taffyjs/wasm` | ✅ | ✅ | ✅ (without permission flags) | +| `@taffyjs/yoga` | ✅ | ✅ | ✅ (local `node_modules`; `--allow-env`, `--allow-read`, and `--allow-ffi`) | +| `@taffyjs/yoga-wasm` | ✅ | ✅ | ✅ (without permission flags) | ## Documentation diff --git a/apps/website/yoga-wasm/index.md b/apps/website/yoga-wasm/index.md index e5e62c7..9d7c2f4 100644 --- a/apps/website/yoga-wasm/index.md +++ b/apps/website/yoga-wasm/index.md @@ -37,6 +37,6 @@ const Yoga = await loadYoga(); ## Runtime and compatibility -The package supports Node.js 22.18 or newer and modern bundled browsers within the [`@taffyjs/wasm` runtime boundary](../wasm/index.md). Direct CDN imports, legacy browsers, and non-browser edge runtimes are not currently supported. +The package supports Node.js 22.18 or newer, Bun 1.2+ within the Bun 1 major, Deno 2.2+ within the Deno 2 major without permission flags, and modern bundled browsers within the [`@taffyjs/wasm` runtime boundary](../wasm/index.md). Direct CDN imports, legacy browsers, and non-browser edge runtimes are not currently supported. Its public API, declarations, and compatibility classifications come from the same source as `@taffyjs/yoga`. Changing to WebAssembly does not add missing Yoga semantics; consult the same [Yoga 3.2.1 compatibility guide](https://github.com/hyfdev/taffyjs/blob/main/packages/taffyjs-yoga/COMPATIBILITY.md). diff --git a/apps/website/yoga/index.md b/apps/website/yoga/index.md index 7c2ec8f..2fcf35a 100644 --- a/apps/website/yoga/index.md +++ b/apps/website/yoga/index.md @@ -1,6 +1,6 @@ # `@taffyjs/yoga` -`@taffyjs/yoga` is a Yoga 3.2.1 compatibility facade backed by native TaffyJS. It is for Node.js applications that want to replace `yoga-layout` while keeping the supported Yoga API and import shape. +`@taffyjs/yoga` is a Yoga 3.2.1 compatibility facade backed by native TaffyJS. It is for Node.js, Bun, and Deno applications that want to replace `yoga-layout` while keeping the supported Yoga API and import shape. Read the [Design Philosophy](./design-philosophy.md) to understand what the package preserves, where Taffy remains visible, and how unsupported behavior is reported. @@ -37,6 +37,6 @@ The asynchronous `yoga-layout/load` entry is also preserved. ## Runtime and compatibility -The package is ESM-only and requires Node.js 22.18 or newer. Native target support follows [`@taffyjs/node`](../node/index.md). It does not run in browsers; use [`@taffyjs/yoga-wasm`](../yoga-wasm/index.md) for the same facade over WebAssembly. +The package is ESM-only and supports Node.js 22.18 or newer, Bun 1.2+ within the Bun 1 major, and Deno 2.2+ within the Deno 2 major. Deno requires local `node_modules` plus `--allow-env`, `--allow-read`, and `--allow-ffi`. Native target support follows [`@taffyjs/node`](../node/index.md). It does not run in browsers; use [`@taffyjs/yoga-wasm`](../yoga-wasm/index.md) for the same facade over WebAssembly. Compatibility is pinned to Yoga 3.2.1. Supported calls preserve Yoga's source shape, while known behavioral differences and unsupported features are explicit. See the [complete compatibility guide](https://github.com/hyfdev/taffyjs/blob/main/packages/taffyjs-yoga/COMPATIBILITY.md) before migrating an existing application. diff --git a/packages/taffyjs-yoga-wasm/README.md b/packages/taffyjs-yoga-wasm/README.md index 0258987..b027f7d 100644 --- a/packages/taffyjs-yoga-wasm/README.md +++ b/packages/taffyjs-yoga-wasm/README.md @@ -4,7 +4,7 @@ The [design philosophy](../../apps/website/yoga-wasm/design-philosophy.md) explains why the Yoga-facing model and runtime transport remain separate choices. -The package supports Node.js `>=22.18` and modern bundled browsers within the [documented `@taffyjs/wasm` runtime boundary](https://github.com/hyfdev/taffyjs/blob/main/packages/taffyjs-wasm/README.md). Browser consumers need a bundler that honors package export conditions and top-level await. Direct CDN scripts, legacy browsers, and non-browser edge runtimes are not currently supported. +The package supports Node.js `>=22.18`, Bun 1.2+ within the Bun 1 major, Deno 2.2+ within the Deno 2 major, and modern bundled browsers within the [documented `@taffyjs/wasm` runtime boundary](https://github.com/hyfdev/taffyjs/blob/main/packages/taffyjs-wasm/README.md). Deno requires no permission flags. Browser consumers need a bundler that honors package export conditions and top-level await. Direct CDN scripts, legacy browsers, and non-browser edge runtimes are not currently supported. This package is still private and unpublished. The installation form below describes the intended registry substitution after publication. diff --git a/tests/taffyjs-yoga-wasm/runtime-smoke.mjs b/tests/taffyjs-yoga-wasm/runtime-smoke.mjs new file mode 100644 index 0000000..edf1513 --- /dev/null +++ b/tests/taffyjs-yoga-wasm/runtime-smoke.mjs @@ -0,0 +1,22 @@ +// Keep this runtime-only harness as JavaScript so clean type-aware checks do not resolve +// the build-only package entries before the WASIP job produces them. +import Yoga from "@taffyjs/yoga-wasm"; +import { loadYoga } from "@taffyjs/yoga-wasm/load"; + +import { runYogaRuntimeSmoke } from "../runtime-smoke.ts"; + +const packageName = "@taffyjs/yoga-wasm"; +const loadEntryName = "@taffyjs/yoga-wasm/load"; +const resolvedEntry = import.meta.resolve(packageName); +const resolvedLoadEntry = import.meta.resolve(loadEntryName); + +if (!resolvedEntry.endsWith("/packages/taffyjs-yoga-wasm/dist/index.js")) { + throw new Error(`${packageName} resolved to an unexpected entry: ${resolvedEntry}`); +} + +if (!resolvedLoadEntry.endsWith("/packages/taffyjs-yoga-wasm/dist/load.js")) { + throw new Error(`${loadEntryName} resolved to an unexpected entry: ${resolvedLoadEntry}`); +} + +runYogaRuntimeSmoke(packageName, Yoga); +runYogaRuntimeSmoke(loadEntryName, await loadYoga());