github shakacode/react_on_rails v17.2.0.rc.0

pre-release3 hours ago

Added

  • [Pro] React 19.3 support with react-on-rails-rsc@19.3.1-rc.0: The node renderer startup
    check and react_on_rails:doctor now accept react-on-rails-rsc 19.3.1-rc.0 (npm next) and later
    19.3.1+ releases with React/React DOM 19.3.x. The react-on-rails-pro optional peer range admits
    ~19.3.1-rc.0. Published react-on-rails-rsc 19.3.0 (npm latest, which bundles React 19.2.8 Flight)
    stays supported with React 19.2.8+ only, and 19.2.x stays on React 19.2.7+. Each check rejects mixed pairs
    such as 19.3.0 with React 19.3 or 19.2.x with React 19.3, and it rejects prerelease React builds. Doctor's
    Fix command names the React version that matches the installed RSC package. The generator still installs
    stable react-on-rails-rsc 19.2.1. Action required for upgraders: apps generated by 17.1.0 pin the
    superseded react-on-rails-rsc 19.3.0-rc.4, which the node renderer now refuses at startup. Install 19.3.0
    with React/React DOM 19.2.8+, or 19.3.1-rc.0 with React/React DOM 19.3.0. The node renderer error and Doctor's Fix name the stable
    release to install for the known 19.3.0 transition; other rejected prereleases no longer imply that
    a compatible stable version is published. Doctor falls back to the published generator pin for those versions.
    #5094 by
    justin808.

Fixed

  • [Pro] RSC streams no longer encode consumer logs into the Flight payload: React 19.3 development
    Flight keeps a console hook active while it flushes chunks, so logs from code consuming the returned RSC
    Readable were serialized as :W["log"...] rows. In non-production builds, RSC payload streams now use
    Node's native console while chunks reach the consumer. Production builds and HTML streams keep the
    caller's console unchanged. #5094 by
    justin808.

  • authenticityHeaders() no longer mutates its input object: The helper now returns a new merged object instead of writing CSRF headers into the caller's otherHeaders argument. Previously, passing a shared or module-level headers object would bake a stale CSRF token into it, causing intermittent 422 InvalidAuthenticityToken errors after Turbo navigations. Fixes #5028.

  • Version checking now resolves the installed package version from pnpm, bun, and Yarn Berry lockfiles:
    pnpm-lock.yaml (lockfileVersion 5.4/6.0/9.0, including the pnpm 11 multi-document form), bun.lock
    (lockfileVersion 0-2, JSONC), and Yarn Berry yarn.lock (__metadata versions 4-8) join the existing Yarn
    classic and package-lock.json support, with the same package.json fallback and precedence behavior as before —
    so semver ranges like ^17.0.0 in package.json no longer fail Rails boot for pnpm, bun, and Yarn 2+ users.
    The binary bun.lockb is not parsed — migrate with
    bun install --save-text-lockfile --frozen-lockfile --lockfile-only, or pin the exact version. A malformed or
    unreadable lockfile falls back to the package.json version instead of erroring, and the non-exact-version error
    text now explains the relaxed installed-version rule instead of forbidding semver ranges outright. Fixes
    #5049.
    #5058 by
    AbanoubGhadban.

  • bin/dev kill now verifies every app-scoped Overmind endpoint within a bounded control budget:
    shutdown discovers all tmp/sockets/overmind*.sock endpoints, fails closed when discovery or probing cannot be
    completed, and terminates and reaps timed-out control clients under one shared deadline per phase. When no renderer
    port is exported, recognized generated Procfiles supply the local Node Renderer fallback without widening the kill
    scope to an unrelated listener on port 3800. Fixes
    #4944. #4986 by
    justin808.

  • Ruby type signatures now match supported helper inputs: RBS accepts the compatibility
    immediate_hydration: option on Controller#redux_store, Pathname locale directories,
    Hash prerender props, and asset-checker/compiler injection keywords. Package-manager detection
    declares its actual symbol results, while invalid inputs remain rejected by runtime type checks.
    Fixes #5037.

  • Configuration RBS signatures match supported values: Runtime type checking now accepts the documented
    Pathname directories, unset configuration values, and the check_database_on_dev_start keyword without changing
    runtime behavior. Fixes #5036
    by justin808.

  • [Pro] TieredCacheHandler keeps serving L1 hits for entries older than l1MaxTtlSeconds: Promoting an
    L2 hit into L1 rewrote the entry's revalidate but kept its original timestamp, so any entry older than
    l1MaxTtlSeconds was written to L1 already expired — permanently bypassing L1 for that key and paying an L2
    (e.g. Redis) round trip on every request. Promoted entries now expire at the earlier of the original entry's
    expiry and l1MaxTtlSeconds from promotion time, and entries with no remaining lifetime skip the L1 write.
    A non-positive l1MaxTtlSeconds now disables L1 entirely instead of storing permanent L1 entries.
    Action required for upgraders: if you run a persistent L1 (e.g. RedisCacheHandler) with a
    non-positive l1MaxTtlSeconds, the disabled L1 retains entries written before it was disabled —
    flush that L1 store before re-enabling it with a positive cap, or retained stale/indefinite entries
    become readable again.
    With the default in-memory L1 this was purely a performance bug; a RedisCacheHandler L1 could
    additionally serve entries past their intended expiry, because Redis applies TTLs at write time —
    promoted entries now always encode exactly the remaining lifetime. Fixes
    #5027.
    #5029 by
    AbanoubGhadban.

  • Console replay can no longer swallow the rest of the page, and replayed messages are no longer altered:
    A server-side console.log argument containing <!-- could switch the browser's HTML parser into a state where
    the console-replay <script> consumed the remainder of the document. The replay code is now escaped with the same
    lossless two-part escape used by Pro's RSC payload injection (<!-- → <\!--, </script → </\script), shared
    as one helper across core and Pro, so the browser console now receives the original logged text (previously
    </script was rewritten to (/script). Fixes
    #5034.
    #5035 by
    AbanoubGhadban.

  • [Pro] Standalone upgrades preserve customized bundler configurations: The Pro generator automatically
    upgrades only complete, unchanged configuration pairs from supported current templates. Customized, historical,
    missing, or ambiguous pairs remain unchanged with manual migration instructions, preventing helper redeclarations
    and partial upgrades. Fixes #4789.
    #5010 by justin808.

  • RSC agent guardrail upgrades now preserve complete files and clean up their stale hook groups:
    The installer atomically replaces copied skill and hook files while preserving their existing permission behavior,
    and removes PostToolUse groups only when removing a managed guardrail hook leaves the group empty. Unrelated
    Claude settings and hook groups remain unchanged. Addresses items 1 and 2 of
    #4815.

  • Explicit id: nil now uses the configured automatic DOM id behavior: Component rendering no longer omits the
    container id while still reporting that a random id is active. Fixes
    #4993.
    #4998 by
    justin808.

  • bin/dev kill now stops only the current app directory's processes, and verifies they are
    gone
    : the kill path matched command lines machine-wide (pgrep -f rails, pgrep -f overmind,
    pgrep -f ruby.*puma, and friends) and scanned ports with an unfiltered lsof -ti :PORT, so
    running it in one worktree could terminate a Rails server, a Webpack dev server, or an unrelated
    client connection belonging to a different checkout — then printed "All processes terminated"
    without checking that anything had actually stopped. bin/dev now claims a worktree-scoped,
    flock-backed session file (tmp/react_on_rails/dev-session.json) recording the app root it
    belongs to, the process group it leads, and the Overmind endpoint it would use. Session
    publication and kill-reader ownership are serialized through a fixed
    tmp/react_on_rails/dev-session.lock: writers publish a same-directory temporary file with an
    atomic rename while retaining the JSON file's lifetime lock, and a kill reader retains both locks
    through shutdown cleanup so another reader cannot authenticate a stale or recycled process group.
    A concurrent bin/dev start now exits non-zero with retry guidance instead of running unrecorded
    while that shutdown lock is held.
    bin/dev kill shuts that session down through its own Overmind control socket or its own process group,
    escalating from TERM to KILL only for survivors, and reports success only after positively
    observing that the owner, its process group, and its listeners are gone. Fallback port discovery
    is restricted to LISTEN sockets whose process working directory is inside the current app root;
    anything it cannot attribute is reported as a diagnostic and never signalled, and missing,
    malformed, unreadable, or foreign session state fails closed rather than falling back to a broad
    kill. The default-mode port list now covers the Shakapacker dev-server port
    (SHAKAPACKER_DEV_SERVER_PORT, then dev_server.port in config/shakapacker.yml, then 3035)
    instead of a hard-coded 3001, and a session records the ports it actually selected so a kill
    verifies those rather than re-deriving a guess. Processes that deliberately leave the group with
    setsid/daemonization remain out of scope, which is why Overmind (whose tmux server daemonizes) is
    controlled through its socket. Breaking API changes for anyone calling
    ReactOnRails::Dev::ServerManager directly: .development_processes and .kill_running_processes
    are removed, .kill_processes now returns a status symbol (and bin/dev kill exits non-zero when
    it refuses or cannot verify a shutdown, with bin/dev clean refusing to delete bundles in that
    case), .print_kill_summary takes (status_symbol, blockers_array) instead of a boolean,
    .kill_port_processes only signals listening sockets whose process working directory is inside
    the current app root, and .find_port_pids is now restricted to listening sockets (it returns
    those listeners other than pid ≤ 1 and the calling process, and does not filter by app root —
    attribution happens in the kill path). Fixes
    #4846.
    #4937 and
    #4964 by
    justin808.

  • [Pro] Surfaced missing RSC loadable stats in Node Renderer logs: The first missing
    loadable-stats.json read now emits an actionable INFO diagnostic per renderer process while
    preserving fallback and retry behavior without replaying the server path to the browser. Fixes
    #4731.
    #4918 by
    justin808.

  • [Pro] OpenTelemetry propagation and existing-provider attachment now recover cleanly: Rails-to-renderer
    requests replace stale mixed-case W3C propagation headers with each request attempt's current client-span context
    without mutating caller-owned raw headers. Node Renderer attachment to an application-owned provider now rolls back
    a newly installed tracing adapter when a conflicting sub-span integration rejects installation, so initialization can
    be retried after the conflict is removed. Existing-provider mode also reports every ignored renderer-managed option
    before provider and context checks, while both provider modes share one service-name precedence implementation.
    #4956 by
    justin808.

  • [Pro] Raised the jwt floor to >= 2.8 for reliable offline license validation on Ruby 3.4+:
    jwt 2.8 first declares base64 as a runtime dependency, so the gemspec now requires jwt >= 2.8, < 4.
    jwt 3.x remains supported and 4.x remains unsupported. Documentation now distinguishes the Pro Node renderer's
    default Fastify 5 setup, which requires Node 20+ at startup, from its engines.node floor of Node 18.19.0+
    when applications use the documented Fastify 4-compatible dependency overrides. Fixes
    #4730.
    #4864 by
    justin808.

Changed

  • [Pro] License: React on Rails Pro moves to The React on Rails Pro License 3.0, an application of ShakaCode
    Trust-Based Commercial Licensing that matches ShakaPerf: free in production for small organizations, charities,
    educational institutions, and hospitals; 45-day production evaluation; 30-day grace after a subscription lapses;
    the license key stays optional and only sets the attribution status.
    Published gem metadata now uses LicenseRef-ReactOnRailsPro, and generator messages reflect these terms.
    #5104 by
    sashakhar1.

Don't miss a new react_on_rails release

NewReleases is sending notifications on new releases.