Skip to content

feat(node)!: add per-node measurement - #47

Merged
hyfdev merged 3 commits into
mainfrom
agent/per-node-measure
Aug 17, 2026
Merged

hyfdev merged 3 commits into
mainfrom
agent/per-node-measure

Conversation

@hyfdev

@hyfdev hyfdev commented Aug 17, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • add TaffyTree.setMeasure(node, measure | undefined) for persistent per-node measurement
  • unify layout behind computeLayout({ root, availableSpace, measure? }) and remove the public computeLayoutWithMeasure method and options type
  • keep callbacks in a wrapper-owned JavaScript map while Rust stores only independent context/measure presence markers
  • filter unmarked leaves before NodeId/context conversion, lazy getStyle provider creation, or Node-API/WASI entry
  • map Yoga measure setters directly to setMeasure and always use the unified computeLayout path without a global fallback
  • update generated declarations, behavior/type tests, documentation, and the vouched binding decisions

Behavior

Measurement dispatch is:

  1. the node's configured per-node callback
  2. the current call's optional global fallback
  3. ordinary native leaf sizing without entering JavaScript

The raw native fast path remains active only when the tree has no configured per-node callbacks and the call has no fallback. A non-undefined, non-function fallback throws TypeError before native computation.

Every setMeasure call, including replacement with the same function and clearing, updates native metadata first and dirties the node before committing the JavaScript map. Context and measure presence remain independent, and removal/clear release retained callbacks.

Global fallback identity and presence are deliberately not part of Taffy's cache key. Adding, removing, or changing a fallback requires dirtying every potentially affected leaf; markDirty(root) does not clear descendant caches. A focused regression test protects this behavior.

Breaking change

tree.computeLayoutWithMeasure({ root, availableSpace, measure });

becomes:

tree.computeLayout({ root, availableSpace, measure });

ComputeLayoutWithMeasureOptions is replaced by ComputeLayoutOptions<TContext>, whose measure property is optional.

Callback evidence

One coding-agent-chat initial layout contains 51 measured text leaves and 115 fixed leaves:

path Node callbacks WASI callbacks fixed-leaf callbacks
global fallback baseline 1,262 1,262 230
per-node registration 1,032 1,032 0

The 1,032 real measured requests are unchanged; per-node marking removes all 230 fixed-leaf JavaScript entries. Diagnostic instrumentation was removed.

Benchmarks

Three matched local rounds on Node v24.19.0, using the unchanged coding-agent-chat initial-layout transaction:

runtime median before median after ops/s before ops/s after
@taffyjs/node 6.7232 ms 5.9531 ms (-11.5%) 145.2 165.2 (+13.8%)
@taffyjs/wasm 40.7591 ms 35.8398 ms (-12.1%) 24.24 27.18 (+12.1%)

benchmarks/results/published.json is unchanged.

Validation

  • vp check: formatting, lint, and types passed with zero warnings
  • vp run check: Rust 11, native binding 45, @taffyjs/node 214, Yoga 557 passed / 85 expected failures / 17 skipped
  • vp run check:wasm: WASI public API 214, Yoga-Wasm 557 passed / 85 expected failures / 17 skipped, plus browser, package, type, and website checks
  • bounded adversarial review and targeted follow-up: PASS

Fixes #44

@hyfdev hyfdev changed the title feat(node): add per-node measure functions feat(node)!: add per-node measurement Aug 17, 2026
@hyfdev
hyfdev marked this pull request as ready for review August 17, 2026 16:43
@hyfdev
hyfdev merged commit b6fbb8b into main Aug 17, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add per-node measure functions to TaffyTree

1 participant