Packages

octane

0.14.3 · 21 Oct 2026MIT19k / weekRepositorynpmAtom feed

105 stable releases, newest first, by release line. Notes are quoted as the maintainers published them.

  1. 0.2.x
  2. 0.2.8

    11 Sep 2026
    • 8a3da65: Avoid scheduling a capture fallback task for ordinary delegated discrete events when no controlled form restoration is pending. Preserve the fallback for a controlled edit whose native bubble is stopped below the root, including controls armed after capture and restores queued by nested events.

    • 70c29ab: Avoid emitting a parallel useBatch for proven module-level context-only reads in TSRX and plain TypeScript. Mixed promise reads and child warming continue to batch as before.

    • 9b23f6c: Avoid building prefixed string keys for unkeyed top-level descriptor lists.

  3. 0.2.7

    9 Sep 2026
    • 52ac66c: Avoid repeated ancestry scans when ordering effects and ref attachments from nearby components.

    • 1158956: Skip repeated class writes for fresh array, object, and function values when their composed class string has not changed. Preserve class composition, hydration, and SVG attribute behavior.

    • 7f3c096: Update the shadcn registry baseline to 4.21.0 and adopt cn 0.2.6 across the Base UI, Radix, and React Aria bindings. Add Base UI Select, Navigation Menu, and Scroll Area wrappers using the release's Nova styles and Base UI 1.8.0 primitives.

      Hoist and deduplicate Base UI's scrollbar stylesheet. Preserve global CSS rules in compiled Float style resources so selectors remain active instead of being removed by scoped-style pruning.

      Update React Aria Components to 1.20.0, React Aria to 3.51.0, and React Stately to 3.49.0. Add TokenField and PreviewTrigger, keyboard shortcut handling, context menus, and the coordinated accessibility and localization fixes.

  4. 0.2.6

    7 Sep 2026
    • f1e6e61: Update @tsrx/core to 0.1.69 and remove the local patch now that the generic-arrow diagnostics and typed-default source-mapping fixes are published upstream. This also includes the shared iterable runtime fixes from 0.1.68.

  5. 0.2.5

    6 Sep 2026
    • 1846318: Require Octane 0.2.5 or newer for testing-library and the Base UI 1.8 binding. Published 0.2.4 does not export isInActScope. Restore the root useMediaQuery export and keep its options argument optional.

    • 1846318: Support library components that use transitive and method-based custom hooks, typed namespaces, and generic interfaces in Octane source. Preserve committed render-phase state when a render suspends, retain server DOM and hydration data while a resolved Suspense boundary waits for client data, and support portals into document fragments and shadow roots.

      Make testing-library rendering and hydration settle native effects consistently, and accept the full Octane renderable input surface.

      Batch nested act callbacks and testing-library rerenders within their outer callback. Await the complete promise queue before resolving act, including with frozen timeout clocks, so asynchronous positioning updates settle before assertions. Expose isInActScope for testing helpers to preserve this batching.

      Render synchronous iterable template loops on the client and during hydration, including sets and generators, while preserving the array reconciliation path.

      Run native event handlers outside component render scope when a DOM update synchronously dispatches an event, such as blur from disabling a focused input.

      Complete finite layout-effect update cascades before publishing DOM mutations to observers, including scheduled updates and repeated measurements in one component.

      Preserve optional method-hook chains, including skipped arguments, method receivers, and short-circuit boundaries in both compiler emission paths.

      Retain resolved Suspense native data in the public SSR result as well as its boundary hydration payload. Retire four Floating UI expected failures now covered by passing upstream ref and positioning assertions.

      Enforce the existing external-store snapshot stability contract during commit cascades. Uncached Zustand object selectors reach the update-depth guard; use useShallow to cache their selected values.

      Require Octane 0.2.5 for the updated Base UI, Base UI Utils, shadcn, and testing-library packages so the compiler and isInActScope API are available. Preserve exact server catch-node adoption when an initially resolved Suspense arm contains a rejected resource.

    • 45f9761: Clear an owned keyed list of inert host rows with one DOM removal after the root render commits. Retain connected rows for suspension rollback and keep the existing per-row teardown for lists with effects, refs, nested scopes, or portals.

  6. 0.2.4

    6 Sep 2026
    • aec5373: Reevaluate Octane app configs safely across environment changes and concurrent builds. Correct form controls, hoisted head metadata, and descriptor-children lexical shadowing. Align Window and Day Picker bindings with Octane's types and native events, and honor falsy Redux server state.

    • 1f19beb: Prevent production API errors and static-file symlinks from disclosing server details or files outside the built asset tree. Preserve injected HTML and settle streaming SSR when callbacks fail, and compile imported descriptor-children components correctly through Rspack and Rsbuild.

    • d38cd81: Allow a scoped <style> inside a split <Hydrate> child when its whole lexical style scope — the block and the host elements it stamps — sits inside the boundary. Client and server keep the authored-position hash. A scope that straddles the boundary is still OCTANE_HYDRATE_SPLIT_STYLE.

  7. 0.2.3

    4 Sep 2026
    • 6135083: Keep the form-action submit handler, and the transition graph it starts, out of production bundles that never install a function form action, and emit the compiled setup checkpoint only for component setups that can schedule a render-phase self-update.

    • 0705d6c: Reuse staged transition updates instead of repeating map lookups.

    • 415e7fd: Recover server rendering throughput: a render whose hoisted head is empty no longer scans the whole response for a </head> unless it is a document. Rendered output is unchanged.

  8. 0.2.2

    2 Sep 2026
    • 6a6dbc1: Fix controlled value/checked edits being reverted before their handlers ran whenever an onXxxCapture handler for the same event type was registered anywhere in the app. The browser runs a microtask checkpoint after every listener of an event it dispatches itself, so the capture segment's stopped-propagation fallback fired between the root's capture listener and its bubble listener and snapped every typed character back to the rendered value. Trusted events now close that window with a task, after native propagation has finished; script-dispatched events keep the microtask fallback.

  9. 0.2.1

    2 Sep 2026
    • 34ba45e: Align the delegated-event commit boundary with React's batchedUpdates: the outermost dispatch of a discrete event now flushes synchronously only when a controlled value/checked host armed a state restore during that dispatch. Other handler updates stay in the microtask batch, so a script-dispatched event (dispatchEvent, click(), requestSubmit()) no longer publishes a commit mid-dispatch that React would publish after the dispatching script yields, and native listeners registered by other code observe the same pre-commit DOM under both renderers. Browser-dispatched events are unaffected in practice because the microtask checkpoint runs before the next native listener and the default action. Tests that asserted committed state immediately after a bare dispatchEvent should wrap the dispatch in act() or flushSync(), as with React.

      Controlled value updates now write the DOM property before syncing the value attribute, matching React's updateInput order. A control left non-dirty by a native form reset previously followed the attribute write and kept its non-dirty state; it is now marked dirty by the property write, so later attribute changes cannot drag the live value.

  10. 0.2.0

    2 Sep 2026
    • ddaa8c5: Promote Octane to beta and begin the 0.2 release line.

    • 456ac4b: Preserve element and fragment ref ownership when a suspended root rolls back, so retries detach the previous ref and attach only the committed replacement. Keep native parsing first while accepting valid TSRX syntax supported by the JavaScript parser, without hiding operational failures or malformed input. Update TSRX core to 0.1.63 and adopt its released computed-key source mapping fix.

    • f6bea37: Keep ordinary delegated continuous-event updates responsive while an unrelated async transition Action is pending. Continuous events retain microtask batching, and updates explicitly wrapped in a transition still wait for the Action.

  11. 0.1.x
  12. 0.1.51

    1 Sep 2026
    • 9321d39: Speed up hydrate module slicing in files with many split boundaries and private declarations.

    • fdb711a: Keep bounded scoped-signal trace retention constant-time after its history fills. Inspection still returns the latest events in chronological order as detached records.

    • 5e80135: Reuse the local component Map for name lookups across TSRX renderer boundaries to speed compilation of large modules.

  13. 0.1.50

    30 Aug 2026
    • 157543f: Reuse normalized renderer configuration and compiled filename matchers across TSRX module classifications. Compiler integrations that retain normalized options no longer repeat renderer validation, signature serialization, brace expansion, and regular-expression construction for every source file.

    • 4d13159: Make hydration of deeply nested, coextensive component wrappers scale linearly.

      Hydration now remembers matching nested marker pairs for the lifetime of the adoption pass, resolves compacted range owners through a deferred parent chain, and removes contiguous redundant marker runs in one DOM mutation. A production SSR benchmark at 512 wrappers dropped from 39.2 ms to 4.9 ms while preserving server-node adoption, delegated interaction, logical marker multiplicity, and clean unmount behavior.

    • a944ff3: Make anchorless-safety propagation linear across deep same-module component graphs while preserving the emitted positional anchors and single-root classification.

  14. 0.1.49

    28 Aug 2026
    • 8adc693: Add an opt-in experimental scoped signal engine backed by Alien Signals 3.2.0, with owned async resources, retained values, ready-state adoption, and native compiler read tracking. Expose the nativeReads compiler option through the application and bundler integrations while preserving explicit hook dependency arrays and the external Alien Signals binding.

      The experiment is not a stable API or a release recommendation. Local derived and async hooks remain deferred, and the accompanying evidence distinguishes supplemental compiler, runtime, and browser checks from the acceptance gates for the locked workspace.

      Expose native read ownership and cached activity metadata through the existing DevTools inspector without evaluating signals or retaining a global graph registry. Match the private compiler ABI's CommonJS entry points to the public runtime so native SSR reads use one protocol instance.

      Collect native reads around actual component invocation, including parameter defaults and indirect returns. Track and replay native reads in inferred memos, preserve deferred element inspection and rendering, and revoke live retained results when a contributing data owner retires. Keep held Suspense output, refs, effects, and native subscriptions together until replacement work is accepted.

      Avoid duplicate native collection setup when invocation collection already owns the scope, while preserving independent child retirement, observer restoration, write guards, and stored-value witness replay.

      Preserve nested Suspense ref lifetimes, finish caught deletion cleanup before replacement effects connect, and reveal the latest urgent state when it supersedes every held state update. Register native compiler and server hook diagnostics in the production error catalog and CLI explanations.

    • a51c8c6: Skip native text-change diagnostic AST analysis when authored TSRX cannot contain an input or textarea host.

  15. 0.1.48

    27 Aug 2026
    • 3ca30fc: Cache configured root membership and the sorted language-service root list in TypeScript-backed text inference so repeated warm snapshots no longer scale with unrelated project roots, and expose the regression benchmark through the MCP benchmark runner.

    • efdc8cb: Index component references once when compiling component-heavy modules.

      Component declaration lowering now preserves the same client and server hoisting semantics without repeatedly sanitizing and scanning every growing source prefix.

    • 922df8c: Skip manifest-cache scans for ordinary watched source changes while preserving package-manifest, full-reset, and diagnostic invalidation behavior. Expose the accompanying manifest-cache invalidation benchmark through the Octane MCP benchmark tool.

  16. 0.1.47

    26 Aug 2026
    • af0d999: Drain queued behavior-root interactions with amortized cursor compaction and constant-time pending-adoption bookkeeping so late modules and separately settling async adoptions stay linear while preserving FIFO and reentrant delivery. Expose the accompanying browser benchmark through the Octane MCP benchmark tool.

    • c800a1f: Allow nested TSRX @{ ... } child blocks to contain setup statements, hooks, and no rendered JSX. Setup-bearing blocks now compile as scoped child render bodies in client, server, and hydration output, while render-only blocks remain transparent grouping.

    • c1bb057: Keep compiler-generated local names compact in production modules with many components, reducing compile work and intermediate output size.

  17. 0.1.46

    24 Aug 2026
    • 7e96f71: Reduce streaming server-render work by checkpointing changed Suspense boundaries instead of copying the entire boundary registry for every component. Preserve render-phase retry state, discovery order, hydration seeds, and error handling.

      Avoid general keyed-child bookkeeping for a single owned text node, and avoid reclassifying host subtrees that already require component reconciliation. Keep text identity, foreign DOM ownership, and interrupted-update rollback unchanged.

    • d7226ff: Add an experimental client-only Valdi writer compiler target with an explicit application-provided adapter contract, public compiler option types, and self-contained regression tests. Existing DOM and universal targets remain unchanged; no native runtime or application build integration is bundled.

  18. 0.1.45

    24 Aug 2026
    • 5b1e6a3: Fix missing root onCaughtError reports for first-mount and parent-driven error boundary catches in non-suspending renders. Publish inline reports after the fallback's refs and layout effects commit, preserve the original error, and discard abandoned reports without duplicating existing scheduled-error reports.

    • 31abee5: Reduce generated client component code by sharing scalar-binding comparisons and renderable-child text updates through the private compiler runtime. Eligible repeated host rows retain inline comparisons to avoid extra calls and cache writes on unchanged bindings. Hydration avoids repeating attribute mutations when the server already has the final client value, and list-only reconciliation is separate from common text and function children.

      Skip URL regular-expression checks only when the first character proves that the existing unsafe-protocol pattern cannot match. URL policy, controlled form values, authored evaluation order, mismatch recovery, and context propagation through unchanged child descriptors retain their existing behavior.

    • fd6ce69: Preserve canonical component wrappers across consecutive Vite hot updates so every save refreshes mounted DOM and universal-renderer components while retaining their own hook state. Keep default exports live and reload when an edit removes or invalidates a refresh boundary.

  19. 0.1.44

    22 Aug 2026
    • 9b06e47: Fix duplicated text when hydrating a sole primitive child that the server framed, including spread-bearing hosts and conditional children. Reuse the server Text node while preserving hydration mismatch suppression, native events, and later child updates.

    • 7535acd: Deduplicate binding hook sub-slot derivation behind Octane's shared helper while preserving each binding's slotless and symbol-identity behavior.

  20. 0.1.43

    21 Aug 2026
    • 4b590bd: Improve Activity parity across compiled JSX, element descriptors, server rendering, hydration, and universal renderers. Hidden boundaries now disconnect public refs, preserve the latest authored styles and text, hide logically owned portals, and contain suspended work without activating an enclosing visible fallback. Retained insertion effects replay safely after suspended hidden renders, including memoized children and nested boundaries.

      Support Activity aliases, namespaces, spreads, children props, and ordered keys without changing the direct mode-only compiler fast path. Integrate Activity visibility changes with ViewTransition enter/exit animations and expose native pseudo-element animations through the transition instance.

      Coalesce hidden descendant visibility scans once per render wave and keep optional Activity implementation and ref tracking off unrelated application paths. Add production browser benchmarks and deterministic work, ref, and bundle controls. Octane's synchronous hidden-work scheduling and existing structural-transaction limitations remain unchanged.

    • c0ff085: Keep boolean renderable children empty when updated from text, and correctly reapply anchored child text after a held transition resumes. Explicit string conversion and typed text bindings retain their existing coercion semantics.

    • 6a68a7d: Fold provider-proven immutable CSS-module class strings before template planning. Production Vite builds retain a live class reference in each static subtree so unused and lazy component styles keep their existing delivery boundaries. Mutable default maps remain dynamic unless their CSS provider supplies an explicit immutable-export contract.

  21. 0.1.42

    20 Aug 2026
    • 1581e1b: Skip already-drained scheduled flushes after synchronous native-event commits. Preserve commit-only effects, refs, Fragment bindings, transition finalization, profiling notifications, and microtask ordering.

    • afa3722: Preserve server-rendered descriptor components when a suspended Hydrate boundary resumes. Claim fallback cleanup ranges only after adoption completes, avoiding false hydration mismatch reports while preserving template-owned checks and removing genuinely unmatched content added during suspension.

    • 231e248: Reduce keyed-row selection work for compiler-proven class-only updates. Preserve full row reconciliation for live renderable children and use the correct class setter for statically known HTML, SVG, and MathML templates. Also avoid unnecessary state-update allocations and focus traversal when a document has no focused control.

  22. 0.1.41

    19 Aug 2026
    • 489a886: Remove memo factory and dependency-array allocations from more production client cache hits, including nested expressions, returned JSX, custom hooks, plain TypeScript modules, and explicit hook slots. Preserve factory scope, declaration timing, callback identity, and held-transition rollback/promotion, and avoid the extra useCallback wrapper closure in every runtime.

    • 922b2d4: Avoid redundant external-store snapshot checks when an urgent DOM render is already queued. Keep universal-renderer subscriptions connected across snapshot and getter changes while preserving committed selectors, cleanup, and error handling. Avoid quadratic projection work for universal state-update queues that end in a replacement value.

    • 814a3c1: Recognize unshadowed String conversions as template text and add an opt-in Node-only TypeScript project adapter for string-child inference. Keep conversion calls intact, reject stale source facts, omit uncertain type proofs, and share the same text classification across client compilation, SSR, and hydration. Publish declarations for the compiler and adapter APIs.

  23. 0.1.40

    18 Aug 2026
    • ff9b859: Parse authored TSRX modules through the native oxc-tsrx compatibility layer in Node to reduce compiler latency while preserving Octane's existing AST, source-map, stylesheet, and diagnostic contracts. Browser and other non-Node compiler consumers continue to use the pure-JavaScript @tsrx/core parser.

    • 14b8b40: Update the bundled TSRX compiler to the latest installable release, including fixes for literal less-than text and tokenizer lookahead handling.

    • cc6e5ea: Extend Strong-mode analysis through statically known useCallback, useEffectEvent, and memo-returned functions. Reject Effect Event calls during render and Effect Events in explicit hook dependency lists, while preserving supported hook usage and compatibility-mode behavior.

  24. 0.1.39

    17 Aug 2026
    • 954028b: Stop the compiled @for path from retaining the entire descriptor renderer.

      mountItem decided whether a de-opt list's items render through the plain deoptItemBody by comparing the body function by identity. The comparison is correct and cheap at runtime, but mountItem sits on the compiled @for mount path that every list-rendering application reaches, so naming deoptItemBody from it is a live reference a bundler must honour. Retaining deoptItemBody retains childSlot — the universal renderable-hole dispatcher — and through it the descriptor renderer, FragmentInstance and fragment refs, portals, transitions, controlled-form restoration, focus preservation and the DOM attribute tables. Applications that only ever render compiled templates, and never present a generic renderable hole, shipped all of it.

      The fact is now recorded on the ForSlot as plainDeopt, next to the existing mappedNative flag, and stamped by childSlot where the body is chosen inside the graph that already retains those modules. mountItem reads the flag instead of naming the function. The recorded value is exactly the identity test it replaces: within the de-opt branch the body is deoptItemBody precisely when the compiler supplied no map body and this is not the mapped fallback, because both body-wrapping assignments are guarded by those same two conditions. Both ForSlot literals declare the field, so every slot keeps one hidden class and the stamp transitions nothing.

      Normalized production builds, gzip, via benchmarks/bundle-size:

      applicationbeforeafter
      js-framework rows40,75220,609
      TodoMVC41,29321,835
      chat-stream41,53021,968
      weather-app49,52431,742

      The saving is entirely in the framework chunk (rows 38,380 → 18,237 B); every application chunk is unchanged, because no compiler output changed. The rows bundle drops from 559 surviving top-level declarations to 340 — childSlot, FragmentInstance, deoptItemBody, renderPortalState, startTransition and setAttribute all become unreachable, while reconcileKeyed and mountItem correctly remain. The octane-tsrx application, framework, and total budgets in bundle-size/app-budgets.json, and the reachability ceilings in bundle-size/minimal-budgets.json, are re-recorded against the new floor.

      This restores the reachability that held before the identity tests were introduced; the compiled output and every rendering path are unchanged.

    • 21f4dfb: Compact safe keyed component rows in the Lynx main-thread first screen into shared host-template range commands.

      The compiler now marks component-owned loops for the Lynx main renderer without granting it the broader background template-program capability. The first-screen renderer proves a single-root scalar/event host program, reuses its immutable shape, and sends one existing mount-template-range command per row while preserving every host ID, logical range, listener ID, first-tree snapshot, and background adoption identity. Unsupported, observable, nested, hidden, native-list, resource, and non-scalar shapes continue through the generic command path.

      In production fresh-page AB/BA runs this reduced 10,000-row public FCP from 1,626.1 ms to 1,411.9 ms (13.2%), exact all-row FCP from 1,596.5 ms to 1,408.4 ms (11.8%), and 30,000-row all-row FCP from 4,704.4 ms to 4,269.2 ms (9.3%). Against the merged main baseline, the controlled rows-zero bundle cost is 1,246 Web gzip bytes (0.92%) and 1,824 Lynx gzip bytes (1.12%).

    • 1cb4a19: Give a component that calls a method-style custom hook its own update boundary.

      A component that calls a hook owns that hook's state, so an update the hook schedules re-renders that component and nothing else. That held for useThing() but not for obj.useThing() — and the object-carried shape is how a large part of the React ecosystem exposes hooks: route.useLoaderData(), api.useGetThingQuery(), and every hook returned by a createXContext() factory.

      The componentSlotLite eligibility pre-pass decides whether a same-module component is hookless. Its body walk rejected a component on an unknown call only when the callee was an Identifier, and the free-identifier sweep ahead of it only ever sees bare names, so neither test could observe api.useCounter(): the receiver is api, and the callee is a MemberExpression. A component whose only hook was member-form was therefore classified hookless and mounted through componentSlotLite, a LiteBlockImpl with no block of its own. Its hook cells and their forceUpdate belonged to the parent, so updating that hook re-rendered the parent and every sibling under it, and the component could never bail out of a render it should have been isolated from.

      The slot-injection pass already handles these calls — it wraps them as withSlot(sym, () => obj.useX(...args, sym)) precisely because they are hooks so the two passes disagreed about the same syntax. The eligibility walk now applies the same use[A-Z] convention to the property name, and fails closed: matching only forfeits the lite path, never correctness.

      Components that genuinely have no hooks keep componentSlotLite exactly as before. The benchmarks/codegen-size corpus compiles byte-identically (raw 152564, min 75164, gz 26774 before and after), and compile time over 200 repetitions is unchanged within measurement noise (baseline 359.3-366.3 ms, candidate 362.2-372.0 ms across interleaved best-of-7 runs). Where the fix does apply, one componentSlotLite call becomes a componentSlotVoid call — on the regression fixture, +28 bytes of emitted output for the component that gains a real block.

  25. 0.1.38

    14 Aug 2026
    • 0635af6: Complete React-compatible development diagnostics for unknown DOM properties, controlled form conflicts, invalid HTML nesting and hydration recovery, native and custom event listeners, and client/server resource hint arguments. Preserve Octane's native-event authoring model while keeping diagnostic helpers and full warning messages out of optimized production bundles.

  26. 0.1.37

    13 Aug 2026
    • 954c75f: React Float conformance evidence, plus two small fixes it surfaced.

      The 121 planned ReactDOMFloat-test.js parity-ledger cases are all dispositioned: 59 now carry executable adapted evidence (41 newly ported conformance tests across three suites plus links into the existing Float/hint suites), 54 are documented non-goals/divergences with per-case rationale (suspensey commits, whole-document containers, Fizz bootstrap/external-runtime protocol, <img>-preload scanning, SuspenseList, shadow-root scoping, streamed-boundary head content), and 8 stay planned against two newly-filed engine gaps, three warning-message families, and server-side hoistable prioritization ordering.

      Fixes: the document-metadata hoist partition is now HTML-scoped — nothing hoists from an SVG lexical context (a precedence link inside <svg> no longer becomes a stylesheet resource; foreignObject re-enters the HTML rules) — and preinitModule with an invalid as now warns in development as documented instead of failing silently.

    • 94fa199: Tear down removed collapsed template runs with one destroy-run command.

      Clearing a 10,000-row keyed table shipped a 100,000-command teardown stream (3.5 MB) from the background thread, acknowledged it with 70,000 per-host tombstone deltas (2.9 MB), expanded every removed collapsed row into seven logical records just to enumerate those commands, and left 70,000 generation tombstones per clear cycle on both threads. All four costs were O(hosts) for an operation whose input is O(runs).

      The universal renderer now advertises a teardownRuns driver capability. When the driver accepts it, a removed subtree that is still a collapsed program-run instance with implicit contiguous ids skips expansion entirely and contributes to one destroy-run command per contiguous range — the driver derives the event unbinds, removals, and post-order destroys from the program it already holds. Rows with refs, explicit ids, portals, or host callbacks keep the explicit per-host path, as does any driver that does not advertise the capability (the DOM boundary is unchanged).

      The Lynx binding negotiates the capability end to end (a new teardown-run readiness request base), expands the command against its dense record store to re-enter the certified teardown fast path — falling back to an accepted-records walk for reordered or explicit-path rows and to the general command loop for partial ranges — and acknowledges a full-run teardown with a single remove-run delta. Both threads then record one sorted retired-range tombstone instead of 70,000 map entries, and the background client retires compact metadata without materializing the handles it never observed.

      On the shared 10,000-row table, the clear command stream drops from 3,514,556 B to ~370 B, the clear acknowledgement from 2,929,786 B to ~813 B, and a create/clear/create cycle keeps the compact create acknowledgement from the previous change. Reused id ranges keep bumping generations, partial-range teardowns and rollbacks are pinned by new host-, client-, and emitter-side regression tests, and the existing certified-teardown and remount suites run unchanged.

    • c2e77a3: Keep plain function overload signatures non-ambient in the virtual TSX.

      esrap before 2.3.2 printed declare on every bodyless function declaration, so a plain overload pair next to its implementation typechecked as TS2384 ("Overload signatures must all be ambient or non-ambient") in the editor and under tsrx-tsc, on source that compiles and runs fine. The dependency floor now requires the fixed printer; an authored ambient declare function keeps its modifier.

  27. 0.1.36

    12 Aug 2026
    • 972fdd3: Reduce temporary allocations during keyed list reorders by safely reusing bounded numeric scratch buffers across reconciliations.

    • 4a792e3: Allow universal renderers to route compiler-emitted thread-function helpers through an optional cold runtime module.

      Lynx now uses that boundary to omit main-thread worklet registries and call bridges from applications that compile no worklets, while retaining late-chunk activation and the existing worklet-enabled behavior.

    • 581b8bd: Reduce hydratable server-rendered HTML by reusing component-owned keyed-list ranges, self-delimiting pure-host descriptor items, and proven host-only conditional or switch ranges. Keep streamed Suspense segments parser-safe without expanding ordinary HTML tags or hydration comments.

  28. 0.1.35

    11 Aug 2026
    • 50b7988: Cache proven immutable state-array filter projections and skip redundant single-token class updates while preserving controlled field restoration.

    • 6daa380: Start independent asynchronous children in proven bounded keyed lists during the same discovery wave while preserving existing suspense and request reuse.

    • d2c9e1c: Reuse delegated-event accessors and capture paths, and omit redundant component markers for proven exhaustive switch-root components.

  29. 0.1.34

    10 Aug 2026
    • 78316b4: Publish executable CommonJS conditions for Octane core, Floating UI, Base UI, and Radix while preserving their existing ESM and source-first entry points. Source-package discovery still recognizes those packages when the CommonJS build has not been generated yet.

    • 4e53ef4: Complete React Fragment ref parity across JSX aliases and spreads, direct element descriptors, server rendering and hydration, strongly typed refs, text and portal ownership, events, observers, focus, geometry, document positioning, and scrolling.

    • 4cc7840: Reuse server-provided hydration data before creating compiler-owned async requests, preventing duplicate client fetches while preserving Suspense and external hydration ownership.

  30. 0.1.33

    10 Aug 2026
    • 1fe297e: Match React's autofocus behavior during server rendering, client mounting, and hydration, and restore focus and text selection after DOM updates.

    • db0d495: Add behavior-only roots for server-rendered and externally streamed DOM, with explicit range ownership, readiness, trusted native event adoption, cancellation, and DOM-preserving disposal.

    • 677182d: Avoid reconciling unchanged compiler-cached renderable children while preserving context and hidden-tree effect lifecycles, cache safe derived values in hook-using JSX components, and omit fetch-warming scaffolding from component trees proven to contain no asynchronous work.

  31. 0.1.32

    9 Aug 2026
    • d453832: Reduce server-rendering overhead and managed document metadata while preserving streaming, hydration, and router asset identity.

    • 3152f0b: Reduce universal keyed-scene reconciliation and Three renderer lifecycle and frame-subscriber overhead while preserving object identity, transactional cleanup, and priority ordering.

    • 1c44117: Reduce production application and package-version bundle sizes while preserving profiling, transition, and hydration behavior.

  32. 0.1.31

    8 Aug 2026
    • 80a9c7e: Compiler-inferred component memoization now admits destructured props parameters. function Child({ rows }) is the same one-props snapshot as function Child(props), so production call sites of such components gain the whole-region dependency cache and skip the child entirely when their props are reference-stable — patterns that evaluate expressions of their own (defaults, computed keys), bind current, or use array destructuring still fall back. Call-site eligibility is also no longer vetoed body-wide by an unrelated ref.current or live-import member read elsewhere in the parent: the reads that actually flow into a site (directly or laundered through a local) still reject that site, everything else keeps its region.

    • 62d7f13: Compiler-inferred component memoization now admits spread bags on host elements. <path d={e.d} {...e.attrs}> inside a component body no longer disqualifies that component's region cache or its keyed-list caches: a host spread is one runtime-diffed binding whose bag is reachable only from dependencies the region guard already witnesses, so skipping on unchanged dependencies is exactly the no-op a re-entry would have been. Re-entries keep full spread semantics — changed keys apply, vanished keys clear, and spread-supplied refs and event handlers attach, swap, and detach as before. Spreads on component tags keep failing closed: they build the child's props snapshot, which cached call sites must never construct from a getter-bearing bag.

    • 16df26e: Skip unchanged keyed rows containing nested host-only lists or conditionals while preserving imported dependency invalidation and structured row ownership.

  33. 0.1.30

    8 Aug 2026
    • 10011bb: Reduce temporary allocations during keyed list reorders by safely reusing bounded numeric scratch buffers across reconciliations.

    • 081fa1e: Avoid reconciling unchanged compiler-cached renderable children while preserving context and hidden-tree effect lifecycles, cache safe derived values in hook-using JSX components, and omit fetch-warming scaffolding from component trees proven to contain no asynchronous work.

    • 60004f0: Reduce component-render bookkeeping when mounting compiler-certified keyed host rows while preserving ordinary DOM insertion, hydration, row identity, delegated events, lifecycle behavior, and existing public APIs.

  34. 0.1.29

    7 Aug 2026
    • 8fb7990: Fix hydration of sibling @if and @switch blocks at fragment roots, including components rendered inside keyed @for lists.

  35. 0.1.28

    6 Aug 2026
    • 2b98a33: Universal target: the root component now carries a committed range, so a state update in the root component itself replays through the scoped path instead of falling back to a whole-root attempt (issue #574 follow-up). Replays also adopt committed component subtrees whose props, state, contexts, and code revision are provably unchanged instead of re-rendering and re-drafting them, and child owner claims resolve through a positional fast path when the render keeps its committed order. In the issue's repro shape with state at the root, one press beside 4,000 untouched siblings drops from ~55 ms to ~10 ms, and clean-subtree adoption also flattens the cost of large in-scope replays such as list components re-rendering unchanged items.

  36. 0.1.27

    6 Aug 2026
    • 46e1833: Keep freshly mounted Suspense content hidden when hydration resumes inside a hidden Activity.

    • 5a8e807: Keep hidden Activity DOM hidden when a descendant independently replaces its output.

      State updates, error and Suspense retries, and accepted hot-module updates now reapply the nearest hidden Activity's visibility after rendering. Replacement elements and text remain hidden until the Activity reveals, while authored display and text values are restored correctly on reveal. Activity and Suspense now share hide ownership for overlapping DOM, so either boundary can reveal first without capturing the other's temporary hidden styles.

  37. 0.1.26

    5 Aug 2026
    • 1f01b08: Refresh compiled component output correctly after accepted hot updates.

      Hot refresh now discards the outgoing compiler-owned template and slot layout before mounting the new body, while retaining the component block and its hook state. Exclusively owned markerless output is promoted to a durable component range during refresh, so static markup edits and newly added component calls update immediately in both mounted and hydrated applications instead of leaving stale DOM or throwing during insertion. When an enclosing control-flow branch shares that root as its boundary, the update safely falls back to a page reload instead.

    • 48e2397: Keep universal state updates proportional to their retained owner subtree: a leaf setState replays only its owning component, keyed-list item state and several owners updated by one event replay their nearest shared component ancestor instead of the root, updates under an idle @try/Suspense boundary stay scoped (active episodes and retained-hidden content still replay from the root, and a scoped render error falls back so the boundary catches it), structural updates that insert, reorder, or remove hosts commit through the scope's physical frame, compact leaf rows driven by list state update within their owning list component, and scoped commits edit the accepted listener tables in place instead of cloning them. Also avoid cloning the object driver's full instance map when preparing a small host batch, and expose the corresponding benchmark through the MCP server.

  38. 0.1.25

    4 Aug 2026
    • bd8bb1b: Require Node.js 22.22.2 or newer across Octane's published packages.

      Add the octane/compiler/register preload for running server and SSG scripts directly with Node or Bun. It compiles imported .tsrx/.tsx modules and plain TypeScript custom hooks in server mode without a Vite build. Bun also targets bare octane imports at octane/server in pass-through authored source dependencies, including packages that manage their hook slots manually.

  39. 0.1.24

    4 Aug 2026
    • ec77602: Fix Rspack and Rsbuild development builds crashing while evaluating hot .tsrx modules by aliasing webpack HMR metadata before reading dispose data.

    • 29c5bdb: Track the receiver of one-level method calls in inferred hook dependencies (#542).

      useMemo(() => count.toFixed(2)) used to infer [count.toFixed] — a function that lives on Number.prototype and therefore never changes identity, so the memo stayed frozen at its first value while count moved. The same hazard applied to any prototype method called on a one-level receiver, including instance methods of replaced class instances.

      Neither static alternative is right for every program: depending on count fixes primitives but would re-run props.onChange(...) hooks on every parent render, because props is a fresh container whose own function property is the real dependency. The inferred array now compiles such calls to __methodDep(root, 'name'), a new semi-public runtime helper that picks the comparable value per render: the member when it is an own property of the receiver, the receiver when the method is inherited, and undefined when the property is absent (so props.onReady?.() stays inert until a handler is passed). Own-property callbacks and absent optional handlers keep exactly their previous recompute behavior; inherited-method calls now correctly recompute when the receiver changes.

      Deeper callees (a.b.c(...)) and computed callees (a[k](...)) already tracked their receivers and are unchanged, as are explicit dependency arrays.

    • 9b032d8: Treat closures marked with an other-context directive as opaque to dependency inference (#542, problem 2).

      A nested function whose directive prologue declares that its body executes outside render — 'use gpu' (TypeGPU shader code) or 'worklet' (Reanimated/worklets-core UI-thread code) — now contributes only its root captures to an inferred dependency array. Previously the array hoisted the closure's member reads to render time, which evaluated context-bound getters such as TypeGPU's .$ where they are illegal, forcing an explicit dependency array. The TypeGPU example from the issue now infers [root, timeUniform, hueUniform] with no array written.

      The directive list is a deliberate allowlist and can grow. Same-context hints ('use strict', React Compiler's 'use memo'/'use no memo') and directives reserved for other semantics ('use server', 'use client', 'use cache', 'use workflow') never truncate; closures without a listed directive keep today's member-path inference.

  40. 0.1.23

    2 Aug 2026
    • c1ad31b: Compile a @{ … } body that returns without reaching an output node.

      A shorthand block whose statements return but which never reaches a trailing JSX node crashed the client compiler with Cannot read properties of undefined (reading 'type'):

      export function CoreGameFunction(props) @{
          const [boardData, setBoardData] = useState([null, null, null]);
          return null;
      }

      @{ … } desugars to a trailing return, so a body carrying any value return is compiled as one whose output is a returned value rather than an emitted template. That classification is driven by the returns alone, but lowering the tail assumed the block also had a render node to lower, and the parser leaves that node null when the block ends on a statement. The crash was not specific to useState or to null: a block holding only return <div /> failed the same way, as did one holding only return 1. It was client-only — the server compiler already treated the same shape as having an empty tail.

      A block with no output node now compiles as what it is: the body's own returns are the whole output, and falling off the end renders nothing. Setup still runs, so slot-keyed hooks keep their state across whichever value the body returned. This is the shape a component is in while it is being written, and the shape a React-style return <jsx> inside a block already had.

      The synthesized tail is dropped where it cannot be reached — when the block ends in a return or a throw, or in an if/else whose arms both do. Reachability is proven syntactically and nothing subtler is attempted, because keeping the tail is always correct: a body that falls through to undefined is how a compiled body reports that it already emitted its template.

  41. 0.1.22

    31 Jul 2026
    • 43df1f9: Evaluate an inline event handler's arguments when the event fires, not on every render. onClick={() => setData(makeData(1000))} compiled to a { fn, args } bundle whose argument was lifted into the component body, so makeData(1000) ran on mount and on every subsequent render even if the button was never clicked. An argument is now bundled only when evaluating it early is observationally equivalent to evaluating it on the event: identifiers, plain member paths, literals, and operators over those. Calls, fresh array/object/regex literals, mutations, ref.current (the ref attaches after the mount that would read it, so the first event received the pre-attach null), and computed member keys keep the ordinary closure handler.

      Inline handlers that read nothing which can change between renders are now installed once at mount instead of being rebuilt and reassigned on every render, matching what a named handler already received. Handlers inside a keyed @for row are excluded, since a surviving row can be handed a different item without remounting.

    • 7a112b4: Reuse scoped JSX value records when host classification hands them directly to the host's child block, avoiding duplicate descriptor construction while preserving context-scoped resolution.

  42. 0.1.21

    30 Jul 2026
    • 10efc28: Fix hydration mismatch recovery corrupting the DOM when a branch adopts a non-spanning server range.

      When an @if/@switch (or an @if-lowered ternary) hydrates a slot whose server content leads with a nested <!--[-->…<!--]--> pair, the branch adopted that first pair as its own range without checking that the pair spans the whole slot. Against a differently-encoded server shape — for example a legacy value-hole list serialized as an outer pair plus one pair per keyed item — the branch then owned only a prefix of the slot: the next branch swap left the stranded remainder on screen next to the new arm, and re-entering the original arm mounted into detached anchors, rendering nothing (or throwing NotFoundError on insertion, depending on the shape). Recovery must never crash or leak — it is the production safety net for stale server HTML.

      The branch adoption now verifies the nested pair reaches the slot's close marker. When it does not, the runtime treats it as a structural hydration mismatch: it warns in development, discards the server range, and client-builds the branch fresh with hydration suspended for that subtree, so cursor-greedy adoption (such as a keyed list's markerless item path) cannot claim the slot's own close marker.

    • 39bfc49: Fix a crash when a sole-child value hole leaves and re-enters array mode.

      An element whose only child is a {expr} hole renders markerless (the slot owns the whole element), and an array value lazily mints a comment marker pair inside the element to anchor the keyed list. Clearing the slot on a later kind flip swept those markers out of the DOM but kept the slot's references to them, so the next mount anchored on detached comments: flipping array → text/null/host → array crashed with Cannot read properties of null (reading 'nodeType'), and array → component threw NotFoundError while inserting the new content. The owns-parent clear now forgets the swept marker pair, so re-entering array mode re-mints a live pair and every other regime returns to the markerless baseline.

    • 4863b39: Render the non-JSX arm of a ternary child hole instead of discarding it, and stop crashing on a null consequent.

      {cond ? A : B} at a JSX child position lowers to an ifBlock when either arm is a JSX literal. The other arm's value — a keyed .map(…) array, a string, a variable holding an element, a nested ternary, a falsy primitive — was compiled into the branch helper as a bare expression statement, so its value was evaluated and dropped and the hole rendered empty. React renders that arm's value, and server rendering already did too, so the same component could ship content from the server that the hydrating client blanked out. A null consequent ({cond ? null : <Jsx/>}) did not compile at all: the lowering crashed reading the missing branch.

      A non-JSX arm now lowers to the authored-equivalent <>{expr}</>, so the branch renders the value through the fragment's child hole: keyed .map arms take the same keyed fast path as @for, nested ternaries become nested ifBlocks, and primitives (including 0) render as text. A null/false consequent compiles to an ifBlock with an empty then branch, mirroring the long-supported null alternate. The server now claims template-form ternary holes symmetrically (ssrControl + arm ranges, exactly like an authored @if), so the hydrating client adopts the taken arm's DOM — including keyed list arms — instead of relying on shape coincidence. Value-form positions (returned .tsx trees, whose holes the client folds into descriptor value holes) keep their existing ssrChild output byte-for-byte.

  43. 0.1.20

    30 Jul 2026
    • c6370b6: Add octane/testing with clampJsdomScrollTop(), an opt-in helper that clamps jsdom's stored scroll position to its reported range and dispatches the resulting native scroll event.

    • dd272ad: Expose a stable native error-boundary reset ref for renderer adapters.

    • c151b71: Add optional Strong mode for clearer state and ref behavior. Enable it across an application with compiler: { strong: true }, in one module with "use strong", or through the Vite, Rspack, and Rsbuild plugin options. Strong modules reject state updates during render, direct state updates while setting up an effect, and render-time writes to refs, with useLinkedState available for state that should follow another value.

  44. 0.1.19

    29 Jul 2026
    • 9d5d642: fix(compiler): keep octane/compiler free of Node builtins

      octane/compiler re-exported the octane Vite plugin, which pulls node:fs, node:path, node:crypto, and node:module into the subpath's module graph through vite.js and bundler.js. Bundled consumers never noticed — Vite and Rollup tree-shake the unused re-export — but consumers that import the subpath unbundled do: browsers and CDNs like esm.sh and jsdelivr resolve the whole graph, so octane/compiler arrived with ~38KB of bundler code and Node polyfill shims attached, and had to be worked around with a deep path into dist/compiler/compile.js.

      The plugin keeps its two existing homes, @octanejs/vite-plugin and octane/compiler/vite, both of which still export it with types.

      import { octane } from 'octane/compiler' no longer resolves. Switch to:

      import { octane } from '@octanejs/vite-plugin';
      // or
      import { octane } from 'octane/compiler/vite';
    • f469b3f: Use the current TypeScript module-declaration kind field throughout Octane's compiler. Runtime client and server output continues to erase ambient global declarations, while editor to_ts output preserves declare global as a valid, type-checkable global augmentation.

    • ac2ae2f: Enable the existing keyed-list purity and automatic memoization optimizations for components authored with ordinary TSX/JSX returns. JSX lists now reuse unchanged items like equivalent TSRX templates while preserving context updates, captured values, component boundaries, and JSX children semantics. Native dense Arrays retain the optimized path; custom or overridden map methods, sparse Arrays, and additional map arguments preserve normal JavaScript behavior.

  45. 0.1.18

    28 Jul 2026
    • c3ba5e0: compile({ inspect: true }) now claims an authored attribute NAME for the tokens that name the call the attribute lowered to, so navigation tooling can resolve a dynamic attribute in both directions.

      A static attribute is baked into template markup and the template's origins already carried it. A dynamic one has no markup at all — <form action={fn}> survives as setFormAction(el, 'action', …) / ssrAttr("action", …), and <input defaultValue={v}/> as setDefaultValueUncontrolled(el, v), which has no name token whatsoever — and every other token of those calls maps to the value expression. The authored name was therefore unreachable from the output: the helper is a different word and the name literal carries quotes, so neither reproduces the authored text a consumer accepts on an inferred attribution.

      Covers both emitters and every attribute that routes to a helper rather than to baked HTML: form actions, controlled and uncontrolled form props, class, style, autoFocus, boolean, ARIA, data-*, and the generic attribute path.

      Inspection-gated as before — the emitted module is byte-identical with and without inspect.

    • 430061e: Allow runtime host components to render descriptor children produced by TSX and createElement call sites.

    • a21ff46: Tear down an effect when its conditional hook call site is not reached during a successful component render.

  46. 0.1.17

    27 Jul 2026
    • bd31a2d: Derived values are now cached at their declaration. A const whose initializer performs a call during render — where every such call is a value projection by the rule automatic memoization already uses — is lowered to a compiler-owned memo keyed on the component locals it reads, so its identity is stable until those inputs change.

      This is what makes region memoization worth having. A region keys on the identity of what it renders, so a derived value rebuilt on every render defeats its cache unconditionally: memo-wall's value-position wall rebuilt 1,000 descriptors through buildValueRows(items) on every parent re-render, and that call alone was 35% of the operation's CPU profile.

      A calculation is admitted on exactly the callee rule that governs regions, so the two agree. A member call is not cached even when its result is named: virtualizer.getVirtualItems() moves with scroll while the virtualizer instance stays put, and caching it froze a virtualized list mid-scroll. That also means const visible = todos.filter(...) stays uncached — a genuine miss, since todos really is an immutable snapshot, but this analysis cannot yet tell an immutable receiver from a live one. Wrap it in useMemo yourself when the identity matters; recovering it automatically needs receiver provenance and is left as follow-up.

      Also never cached: hook calls (recognised by naming convention, including React's unstable_use* staging prefix — a cache around a hook freezes its state cell and any subscription it owns), let declarations, which stay the escape hatch for a value that must recompute every render, and calculations the render tree never reads. Server compiles are untouched, since a server render evaluates each body once.

      Measured on benchmarks/memo-wall, paired runs against a baseline recorded before any edit:

      parent_rerender_equal_B 0.226ms → 0.091ms (−60%) one_change_B 0.235ms → 0.156ms (−34%) ctx_through_wall_B 0.569ms → 0.413ms (−27%)

      That is the createElement-descriptors-through-a-children-hole shape every @octanejs/* binding produces. todomvc, chat-stream and js-framework compile byte-identically with and without this change and are reported as controls only — their run-to-run movement (up to ±100% on sub-millisecond operations) is the noise floor, not a result.

      Compiled output grows 0.07% gzip on the codegen-size corpus.

    • 9e0ef45: Stop reporting a deleted subtree's cleanup as a render-phase cross-component update.

      Octane discovers a deletion while reconciling the deleting parent's output, so CURRENT_BLOCK still names that parent while the removed subtree's destroys and cleanups run. A cleanup that calls setState on a surviving component was therefore treated as a render-phase update: it logged Cannot update a component (X) while rendering a different component (Y) and flagged the target for the render-phase branch of the update-depth error.

      Those callbacks are mutation-phase work (React runs them in commitDeletionEffects), and updating another component from them is the normal registry pattern, so they no longer take that branch. @octanejs/radix's Select hit this on every open and close: SelectItemText's layout cleanup calls onNativeOptionRemove on the Select provider as the content swaps between its detached fragment and the open popper.

      Genuine render-body updates are unaffected: those run with no cleanup on the stack and still warn and still terminate the loop.

    • dea219b: Stop the de-opt reconciler removing DOM it did not create.

      An element rendered with no children still had its existing DOM reconciled against an empty child list, so anything inserted by other means — a replaceChildren with a captured snapshot, a third-party widget mounted into a container — was swept away by the next unrelated re-render. React only manages children it created, and portal ranges were already excluded here for that reason; this extends the same treatment to imperative content.

      Children the renderer did commit still clear when they go away, so {cond && <X/>} is unaffected.

      Found from @octanejs/base-ui, whose popup Viewport fills a snapshot container this way and lost its previous content mid-transition.

  47. 0.1.16

    26 Jul 2026
    • 85a1c6d: The auto-memo analysis no longer walks each component body twice to collect the imported components it renders and to look for a deferred ref read. Both questions are answered by one traversal, halving the node visits and visited-set allocations those two passes cost. Compiler output is unchanged.

    • f4c97d8: Extend the opt-in compiler inspection surface (compile(source, file, { inspect: true })) so source↔output navigation reaches constructs that leave no trace of themselves in the emit. result.inspect.segments entries now carry exact, marking a segment the compiler itself anchored on an authored span rather than one inferred from the print's map; control-flow directive keywords (@if, @else, @for, @empty, @switch, @case, @default, @try, @pending, @catch), event-attribute names, and scoped <style> blocks claim the code they lowered to through it. result.inspect.templates gains SSR entries (each static run's exact bytes plus its origins) and result.inspect.aliases relates an authored span to the origin that owns its emission. octane/compiler/volar adds compileTypesInspection, a navigation-only sibling of compileToVolarMappings: same parse, same transform, same output bytes, without the Volar mapping layer, so nothing here can perturb the language server. Emitted code and source maps remain byte-identical with the option on or off, and normal compiles skip all recording.

    • f3543bf: The compiler's copy-on-write AST rewriter no longer recurses into properties that cannot be rewritten. Only object-valued properties can produce a replacement, but every type, name and raw string was still passed to a recursive call that returned it unchanged, which was 61% of the walk. Compiles are measurably faster with byte-identical output.

  48. 0.1.13

    22 Jul 2026

    Reading the note…

  49. 0.1.12

    20 Jul 2026

    Reading the note…

  50. 0.1.11

    19 Jul 2026

    Reading the note…

  51. 0.1.10

    18 Jul 2026

    Reading the note…

  52. 0.1.9

    18 Jul 2026

    Reading the note…