Skip to content

Add source-mapped stack traces to the Pro node renderer (#3893 core)#3940

Merged
justin808 merged 40 commits into
mainfrom
jg/3893-renderer-source-maps
Jun 15, 2026
Merged

Add source-mapped stack traces to the Pro node renderer (#3893 core)#3940
justin808 merged 40 commits into
mainfrom
jg/3893-renderer-source-maps

Conversation

@justin808

@justin808 justin808 commented Jun 11, 2026

Copy link
Copy Markdown
Member

Summary

Part of #3893 (core deliverable; keep the issue open for the two docs follow-ups split out by the 2026-06-11 triage: consolidating the existing --inspect pages and the Performance-Tracks guide).

Node-renderer SSR errors now surface source-mapped stack traces — original TS/JSX file:line:column instead of bundled positions — through both error propagation paths (host-caught VM exceptions → exceptionMessage, and stacks serialized inside the bundle by react-on-rails handleErrorrenderingError.stackReactOnRails::PrerenderError). No Ruby changes needed; the remapped stack flows through the existing protocol fields.

Spike conclusion (per the triage's required spike)

  • --enable-source-maps does NOT work for VM-evaluated bundles — verified empirically on Node 22.12.0 and 24.8.0 with external and inline maps and filename set: V8 formats .stack with the Error.prepareStackTrace of the realm the error was constructed in, and Node only instruments the main realm.
  • source-map-support rejected: adds a dependency and has the same realm problem.
  • Implemented approach: a tiny Error.prepareStackTrace installed inside the VM context delegating position lookups to a host-side resolver built on Node's built-in module.SourceMap (zero new dependencies).

Implementation properties

  • Lazy, on error only: prepareStackTrace runs on first .stack access; map I/O happens on the first frame that needs it, then cached per live bundle registration. Evicted VM registrations are retained only while active execution contexts may still lazily format stacks, then the registration and parsed-map cache are released. No per-request map I/O.
  • Security: the resolver exposed to the VM is allowlist-keyed per registered bundle — untrusted bundle code cannot probe the filesystem for arbitrary .map files.
  • Map discovery: inline data: URL, sourceMappingURL file ref, <bundle>.js.map fallback; graceful fallback to original frame text on any failure.
  • Bundle eval now passes filename, so frames name the real bundle path instead of evalmachine.<anonymous> even without maps.

Codex Decision Log

  • Non-blocking: findEntry vs findOriginDecision: findEntry with manual 0/1-based conversion (works across supported Node versions).
  • Non-blocking: codex review found that findEntry returns the nearest previous mapping for unmapped generated lines (cross-line bleed) — Decision: addressed in a follow-up commit; entries are accepted only when generatedLine matches the requested line, with a regression test.
  • Non-blocking: no config flag — behavior is strictly additive and lazy.
  • Non-blocking: registration is kept after buildVM failure so lazily-materialized stacks of bundle-evaluation errors still remap.
  • CodeRabbit must-fix: unbounded source-map registrations after VM eviction -- Decision: execution contexts now retain source-map registrations for active requests; normal, streaming, and incremental close paths release them, and evicted registrations/cache are retired after the last live request finishes.
  • CodeRabbit must-fix: non-base64 data URL maps -- Decision: parseDataUrlSourceMap now accepts both ;base64 payloads and URI-decoded plain data URL payloads.
  • CodeRabbit must-fix: line-4 source-map fixture delta -- Decision: changed the source-line delta from 3 to 2 and added a direct line-4 resolver assertion.

Coordination

Same package as #3939 (/health+/ready); diff is disjoint (vm.ts + new source-map files vs worker.ts/configBuilder.ts) except for trivially-mergeable CHANGELOG.md. Merge #3939 first per the batch plan.

2026-06-13 CodeRabbit follow-up readiness

Current head: 1622aa72a8269130b3fc3710f8e3665c2df364dc. Addressed the three CodeRabbit requested changes from review PRR_kwDOAnNnU88AAAABC8FACQ: request-lifetime source-map cleanup after VM eviction, non-base64 data URL parsing, and the line-4 fixture delta. Optional Claude suggestions remain deferred unless covered by those touched-code fixes.

  • pnpm exec jest tests/vmSourceMapSupport.test.ts tests/vm.test.ts tests/incrementalRender.test.ts tests/handleIncrementalRenderStream.test.ts tests/worker.test.ts --runInBand -> 114/114 pass.
  • pnpm --filter react-on-rails-pro-node-renderer type-check -> pass.
  • pnpm exec eslint packages/react-on-rails-pro-node-renderer/src/worker.ts packages/react-on-rails-pro-node-renderer/src/worker/handleIncrementalRenderRequest.ts packages/react-on-rails-pro-node-renderer/src/worker/vm.ts packages/react-on-rails-pro-node-renderer/src/worker/vmSourceMapSupport.ts -> pass.
  • pnpm exec prettier --check packages/react-on-rails-pro-node-renderer/src/worker.ts packages/react-on-rails-pro-node-renderer/src/worker/handleIncrementalRenderRequest.ts packages/react-on-rails-pro-node-renderer/src/worker/vm.ts packages/react-on-rails-pro-node-renderer/src/worker/vmSourceMapSupport.ts packages/react-on-rails-pro-node-renderer/tests/vmSourceMapSupport.test.ts -> pass.
  • git diff --check / git diff --cached --check -> pass.

Test plan

  • New tests/vmSourceMapSupport.test.ts (16 tests, hand-rolled VLQ maps, no new deps): both propagation paths, external/base64 inline/plain data URL maps, bundle-eval-time errors, Module.wrap first-line column correction with a decoy mapping, unmapped-line fallback (regression for the codex finding), VM-eviction request-lifetime cleanup, no-map fallback, resolver allowlist/input validation. 43/43 pass together with vm.test.ts.
  • End-to-end with the real Pro dummy webpack bundle: staged the actual built server-bundle.js + external map exactly as an upload, ran the rendering request Rails generates for the dummy's SSR-throwing component — renderingError.stack (the exact field Rails feeds ReactOnRails::PrerenderError) reads at HelloWorldWithLogAndThrow (webpack://react_on_rails_pro_dummy/./client/app/ror-auto-load-components/HelloWorldWithLogAndThrow.jsx:24:1) — line 24 is the literal throw line.
  • Full package suite: 334 pass; the 31 failures in 5 suites reproduce identically on pristine origin/main (no local redis, streaming timeouts) — zero regressions.
  • Root pnpm run lint, pnpm run type-check, pnpm run build, pnpm start format.listDifferent: all pass.
  • codex review --base origin/main: one P2 finding, fixed with regression test (see decision log).

🤖 Generated with Claude Code


Note

Medium Risk
Changes sit on the SSR error and VM/stream shutdown paths with filesystem-backed source-map reads, but behavior is additive, lazy, allowlisted, and heavily regression-tested.

Overview
Pro Node renderer SSR failures now report original file:line:column when server bundles ship inline or adjacent source maps. A new vmSourceMapSupport module registers bundles, loads maps lazily on first stack access (Node module.SourceMap), installs an in-VM Error.prepareStackTrace hook, and remaps host-side formatExceptionMessage stacks with an allowlisted resolver. Bundles evaluate with their real filename so frames are not evalmachine.<anonymous>.

VM lifecycle changes add ExecutionContext.release() so source-map registrations survive VM pool eviction while a request is active, then unregister after the response finishes. Standard streaming renders pipe through a progress transform and release on stream end/timeout; incremental renders await async close hooks, time out hung streams, and only release after both request close and response completion.

Hardening: bundle timestamp path components are validated against traversal; error HTTP bodies get text/plain + nosniff. OSS react-on-rails applies an optional __reactOnRailsProRemapStackTrace global when building renderingError metadata and cross-realm convertToError stacks. Docs/changelog and Node >=18.19.0 engine bump accompany broad new tests.

Reviewed by Cursor Bugbot for commit 738c580. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

Release Notes

  • New Features
    • Added Pro support for source-mapped stack traces in the Node renderer, mapping SSR/renderer failures back to original TypeScript/JavaScript locations with lazy loading and per-bundle caching.
    • Introduced an optional global hook to remap rendering error stacks in server render metadata.
  • Bug Fixes
    • Improved error stack formatting for better tooling compatibility and more accurate bundle naming.
    • More reliable execution-context cleanup coordinated with response completion, including safer incremental rendering shutdown with async/timeout-aware handling.
  • Documentation
    • Added a “Source-Mapped Stack Traces” debugging guide section with security cautions.
  • Chores
    • Set Node engine requirement to >= 18.19.0.

Merge Readiness Criteria

Current evaluation as of 2026-06-15 for head 738c580ac5b1839283fcfbaa4795592441caf6c7.

  • Release mode: accelerated-rc, from canonical release gate Release gate: react_on_rails 17.0.0 #3823 (Mode: accelerated-rc).
  • Current head SHA: 738c580ac5b1839283fcfbaa4795592441caf6c7.
  • CI/check status: No required checks are configured, so the full gh pr checks 3940 list is the readiness gate. Current head has 43 passing checks and 6 expected selector/skipped checks; no pending or failing checks. Passing checks include CodeQL, claude-review, renderer package JS tests, node-renderer E2E, dummy node-renderer RSpec, Pro lint, Pro gem specs, examples, precompile, bundle size, docs/sidebar/llms/link/format checks, and CodeRabbit. Skips are selector/confirmation/docs-format/Cursor Bugbot rows with no unresolved review thread.
  • Review-thread status: Final paginated GraphQL review-thread sweep reports unresolved=0.
  • Review feedback triage: Confirmed blockers were fixed and resolved: source-map fallback retry/lifecycle, CodeQL reflected-XSS text response handling, Cursor incremental context release, and Codex diagnostic-preservation feedback. Stale advisory reviews remain advisory only and are not cited as gates.
  • Validation run: git diff --check -> pass; pnpm --dir packages/react-on-rails-pro-node-renderer exec jest tests/worker.test.ts --runInBand -t 'renderingRequest is missing|reports unexpected handleRenderRequest failures once|no assets and no bundles|password is required but no password was provided|password is required but wrong password was provided|invalid JSON in first chunk|missing required fields in first chunk|unexpected handleRenderRequest failures once' -> 7/7 selected pass; pnpm --dir packages/react-on-rails-pro-node-renderer run type-check -> pass; pnpm start format.listDifferent -> pass; pnpm --dir packages/react-on-rails-pro-node-renderer exec eslint src/worker.ts tests/worker.test.ts -> pass (test file ignored by config warning only); pnpm --dir packages/react-on-rails-pro-node-renderer exec jest tests/vmSourceMapSupport.test.ts --runInBand -> 41/41 pass; node script/generate-llms-full.mjs --check -> pass after the final main merge (unchanged by later code-only fixes); pre-commit and pre-push hooks -> pass on the final fix commits.
  • Label/CI decision: Labels: full-ci. Appropriate because this touches Pro node-renderer VM/source-map/error/lifecycle behavior and full CI completed on the current head. No benchmark label is applied by this worker.
  • Known residual risk: Low residual risk remains around Pro node-renderer diagnostic/error response behavior and source-map lifecycle; mitigated by focused local tests plus current-head full CI including CodeQL, node-renderer package tests, E2E, Pro lint, Pro gem specs, examples, and precompile.
  • Merge recommendation: Merge now. Current-head gates are complete, merge state is CLEAN, and maintainer approval was provided in the parent Codex batch thread.

Agent Merge Confidence

Mode: accelerated-rc
Current head SHA: 738c580
Score: 9/10
Auto-merge recommendation: yes
Affected areas: Pro Node Renderer source-mapped SSR stacks, VM/source-map lifecycle, OSS server-render stack propagation, renderer error text responses, generated docs/llms output
CI detector: script/ci-changes-detector origin/main -> JavaScript/TypeScript code and React on Rails Pro Node Renderer package; recommends lint, JS tests, dummy/generator/e2e, Pro lint/tests, Pro Node Renderer tests, and benchmark suites
Validation run:

  • git diff --check -> pass
  • Focused tests/worker.test.ts slice -> 7/7 selected pass
  • pnpm --dir packages/react-on-rails-pro-node-renderer run type-check -> pass
  • pnpm start format.listDifferent -> pass
  • pnpm --dir packages/react-on-rails-pro-node-renderer exec eslint src/worker.ts tests/worker.test.ts -> pass (test file ignored warning only)
  • pnpm --dir packages/react-on-rails-pro-node-renderer exec jest tests/vmSourceMapSupport.test.ts --runInBand -> 41/41 pass
  • node script/generate-llms-full.mjs --check -> pass after final main merge; unchanged by later code-only fixes
  • pre-commit/pre-push hooks -> pass on final fix commits
    Review/check gate:
  • GitHub checks: complete for 738c580; 43 pass, 6 expected skips, 0 failed, 0 pending
  • Review threads: GraphQL unresolved count is 0
  • Current-head reviewer verdicts: claude-review complete for 738c580; CodeQL complete/pass; CodeRabbit pass
  • Stale reviewer verdicts, advisory only: prior Claude/Cursor/Codex/GHAS comments for older SHAs were fixed, resolved, or superseded and are not cited as current-head gates
    Known residual risk: Low; error diagnostics are preserved as text/plain+nosniff and source-map lifecycle has focused coverage plus full CI
    Finalized by: maintainer approval from Justin in the Codex parent batch thread; current-head claude-review, CodeQL, CodeRabbit, and full CI checks provide independent check evidence for this SHA

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds source-mapped stack traces to the Node renderer VM with coordinated execution-context lifecycle management. A new vmSourceMapSupport.ts module maintains a bundle allowlist, lazily loads source maps from inline data: URLs or sidecar files with security checks, and exposes a security-hardened resolveOriginalPosition function. vm.ts integrates source-map support by injecting the resolver into each VM context, installing a V8-compatible Error.prepareStackTrace hook before bundle execution, and managing reference-counted bundle registrations across VM lifecycle. worker.ts and handleIncrementalRenderRequest.ts add explicit context-release coordination tied to response completion for both streamed and non-streamed outcomes. utils.ts applies remapping to exception messages; client-side integration invokes optional host-side remapper on error metadata; tests verify remapping across inline/external maps, eval errors, eviction, resolver security, and incremental streaming context cleanup.

Changes

Source-Mapped Stack Traces in Node Renderer

Layer / File(s) Summary
Source-map support module and registry
packages/react-on-rails-pro-node-renderer/src/worker/vmSourceMapSupport.ts
New module with bundle allowlist, lazy source-map cache (inline data: and sidecar file), directory-escape and symlink-escape prevention, resolveOriginalPosition with strict type and range validation, first-line column-offset support for module-wrapped code, and PREPARE_STACK_TRACE_INSTALL_SCRIPT for VM-realm error formatting; includes remapStackTrace and remapErrorStack utilities.
VM renderer integration with source-map support
packages/react-on-rails-pro-node-renderer/src/worker/vm.ts
Imports vmSourceMapSupport helpers, computes module-wrapper prefix length, injects resolver into each VM context, installs Error.prepareStackTrace before bundle evaluation, registers bundles with wrapper-prefix offset and passes filename to vm.runInContext, remaps stacks on build failures, extends ExecutionContext type with release() method, and implements reference-counted retention/release across eviction and request boundaries; cleanup in resetVM/removeVM.
Execution context lifecycle and response completion coordination
packages/react-on-rails-pro-node-renderer/src/worker.ts, packages/react-on-rails-pro-node-renderer/src/worker/handleIncrementalRenderRequest.ts
Adds helpers to coordinate executionContext.release() with response completion: releases on stream finish (close/end/error events) or in finally blocks; changes IncrementalRenderSink.handleRequestClosed to async, exposes executionContext on sink, awaits VM execution for onRequestClosedUpdateChunk with error logging, and implements timeout-bounded async request-closure coordination.
Exception message stack remapping in VM context
packages/react-on-rails-pro-node-renderer/src/shared/utils.ts
Updates formatExceptionMessage to capture error's raw stack, remap it via remapStackTrace, fall back to original stack if remapping returns nothing, and interpolate the result into exception message.
Comprehensive vmSourceMapSupport test suite
packages/react-on-rails-pro-node-renderer/tests/vmSourceMapSupport.test.ts
Hand-rolled VLQ/base64 and mapping-segment helpers generate deterministic inline/external fixtures with decoy mapping for module-wrapper column validation. Tests cover host-caught and VM-serialized remapping, external .map loading, directory-escape rejection, eval-time errors, unmapped lines, post-eviction lifecycle, missing maps, resolver security, cache invalidation, and prepareStackTrace fallback.
Incremental render execution context lifecycle tests
packages/react-on-rails-pro-node-renderer/tests/incrementalRender.test.ts
Extended test suite with mocked execution context injected into IncrementalRenderSink. New tests verify that release() is deferred until streaming response finishes (not called on request EOF alone), failures in onRequestClosedUpdateChunk VM execution are logged via log.error before cleanup, and response is destroyed when request-close hook exceeds stream timeout.
Client-side rendering error stack integration
packages/react-on-rails/src/serverRenderUtils.ts, packages/react-on-rails/tests/serverRenderReactComponent.test.ts
Adds optional global hook (__reactOnRailsProRemapStackTrace) for remapping RenderingError.stack before building render metadata, with defensive fallbacks on missing hook or non-string results. Test suite includes setup/cleanup and a new case verifying remapped stack text in error metadata.
Documentation, changelog, and Node.js engine requirement
CHANGELOG.md, docs/oss/building-features/node-renderer/debugging.md, packages/react-on-rails-pro-node-renderer/package.json
Changelog entry for the Pro feature (lazy loading, stderr routing, real-file-path evaluation, per-bundle caching); debugging doc section on inline vs external source-map setup, renderer discovery, lazy loading/caching, and caution against public exposure; Node.js engine requirement >=18.19.0.

Sequence Diagram(s)

sequenceDiagram
  participant SSR as SSR Request Handler
  participant CtxMgr as Execution Context<br/>Lifecycle
  participant VM as vm.ts (bundle executor)
  participant PSTT as PREPARE_STACK_TRACE_INSTALL_SCRIPT
  participant Resolver as resolveOriginalPosition
  participant Cache as Source Map Cache
  participant FS as Filesystem

  SSR->>CtxMgr: buildExecutionContext (bundles)
  CtxMgr->>CtxMgr: Retain source-map registrations
  CtxMgr-->>SSR: ExecutionContext {runInVM, release}
  
  SSR->>VM: Run bundle in context
  VM->>VM: registerBundleForSourceMaps (if needed)
  VM->>VM: Install PREPARE_STACK_TRACE_INSTALL_SCRIPT
  
  VM->>SSR: Error thrown (bundle or render)
  SSR->>PSTT: Access error.stack (triggers prepareStackTrace)
  PSTT->>Resolver: resolveOriginalPosition(file, line, col)
  Resolver->>Resolver: Validate input, check allowlist
  Resolver->>Cache: Lookup SourceMap
  Cache-->>Resolver: miss
  Resolver->>FS: Read bundle, parse sourceMappingURL
  FS-->>Resolver: Parsed SourceMap
  Resolver->>Cache: Store SourceMap
  Resolver-->>PSTT: {source, line, column}
  PSTT-->>SSR: Remapped stack trace
  
  SSR->>CtxMgr: Stream response (or complete)
  CtxMgr->>CtxMgr: executionContext.release() on finish
  CtxMgr->>CtxMgr: Unregister source-maps
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested labels

enhancement, documentation, review-needed

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: adding source-mapped stack traces to the Pro node renderer, which is the primary objective of the entire changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jg/3893-renderer-source-maps

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread packages/react-on-rails-pro-node-renderer/src/worker/vm.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 350c3a451b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/react-on-rails-pro-node-renderer/src/worker/vmSourceMapSupport.ts Outdated
@greptile-apps

greptile-apps Bot commented Jun 11, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds source-mapped stack traces to the Pro node renderer's VM layer. Because Node's --enable-source-maps flag does not instrument the separate V8 realm of a vm context, a custom Error.prepareStackTrace hook is installed inside each VM context before the bundle runs; it delegates position lookups to a host-side resolver built on Node's built-in module.SourceMap (no new dependencies). Both error-propagation paths are covered: exceptions that escape vm.runInContext and stacks serialized inside the bundle by react-on-rails's handleError.

  • New vmSourceMapSupport.ts: allowlist-keyed resolver, lazy per-bundle source-map loading (inline data: URL, sourceMappingURL file ref, or <bundle>.js.map fallback), Module.wrap first-line column correction, and a findEntry cross-line-bleed guard. Zero per-request overhead when no error occurs.
  • vm.ts changes: filename option added to vm.runInContext, registration/unregistration wired to pool eviction and resetVM, and the PREPARE_STACK_TRACE_INSTALL_SCRIPT injected before each bundle is evaluated.
  • Tests: 9 focused tests using a hand-rolled VLQ encoder covering both propagation paths, inline/external maps, the column-correction decoy, unmapped-line regression, no-map fallback, and the resolver allowlist.

Confidence Score: 4/5

Safe to merge; the source-map resolver is additive, lazy, and fail-open — all error paths fall back to the original frame text with no change to rendering behavior.

The implementation is well-tested and the logic is sound. Two minor issues are present: parseDataUrlSourceMap silently skips non-base64 data URLs (a non-issue for webpack/rspack but could trip up other bundlers), and sourceMapCache can hold a stale null entry if buildVM fails, log.error materializes the stack, and the same bundle path is later retried with a bundle that now includes a source map. Neither affects production behavior under normal timestamp-based bundle naming, but the stale-cache scenario is reproducible in development iteration cycles.

packages/react-on-rails-pro-node-renderer/src/worker/vmSourceMapSupport.ts — the parseDataUrlSourceMap and resolveOriginalPosition cache-check logic are the two spots worth a second look before closing out the issue.

Important Files Changed

Filename Overview
packages/react-on-rails-pro-node-renderer/src/worker/vmSourceMapSupport.ts New module implementing source-mapped stack traces for VM errors; well-structured with security allowlist, lazy loading, and per-bundle caching. One edge case: non-base64 inline data URLs (rare in practice) silently produce no map. Stale cache can occur if the same bundle path is reused after a failed build, since log.error in the buildVM catch block materializes the stack before retry.
packages/react-on-rails-pro-node-renderer/src/worker/vm.ts Integration of source map support into buildVM: registers bundles before eval, passes filename option, and unregisters on eviction/reset. Ordering is correct — prepareStackTrace is installed before the bundle runs, and registration stays after a failed build intentionally. Clean and minimal changes.
packages/react-on-rails-pro-node-renderer/tests/vmSourceMapSupport.test.ts Comprehensive 9-test suite covering both error propagation paths, inline/external maps, Module.wrap column correction with a decoy mapping, unmapped-line fallback regression, no-map fallback, allowlist rejection, and input validation. Hand-rolled VLQ encoder avoids new dependencies.
CHANGELOG.md New entry added for source-mapped stack traces, but the PR link placeholder [PR XXXX] was not updated with the actual PR number (3940).
docs/oss/building-features/node-renderer/debugging.md Added a concise 'Source-Mapped Stack Traces' section explaining inline vs. external map setup, lazy loading, and the no-map fallback. Accurate and well-placed.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[buildVM called] --> B[Install PREPARE_STACK_TRACE_INSTALL_SCRIPT in VM context]
    B --> C[registerBundleForSourceMaps\nbundlePath + firstLineColumnOffset]
    C --> D[vm.runInContext with filename option]
    D -->|success| E[VM stored in vmContexts pool]
    D -->|throws| F[catch: log.error accesses error.stack\nVM prepareStackTrace fires\nresolveOriginalPosition populates cache]
    F --> G[re-throw: registration kept\nfor lazy stack materialization]
    E --> H[runInVM called per request]
    H --> I{Error in VM?}
    I -->|exception escapes| J[formatExceptionMessage\naccesses error.stack host-side\nVM prepareStackTrace fires]
    I -->|handleError inside bundle\naccesses e.stack| K[prepareStackTrace fires\ninside VM context]
    J --> L[resolveOriginalPosition called]
    K --> L
    L --> M{sourceMapCache hit?}
    M -->|undefined - first call| N[loadSourceMapForBundle\nfs.readFileSync bundle\nextract sourceMappingURL\nparse inline data: or read .map file]
    N --> O[cache result\nnull if no map]
    M -->|cached SourceMap| P[findEntry lineNumber-1, zeroBasedColumn]
    O --> P
    P -->|generatedLine matches| Q[return originalSource:line:col]
    P -->|no mapping or cross-line bleed| R[return null\nkeep bundled frame text]
    Q --> S[Replace file:line:col in frame text]
    R --> T[Keep original frame text]
    E -->|VM evicted from pool| U[unregisterBundleForSourceMaps\nclears registration + cache]
Loading

Reviews (1): Last reviewed commit: "Fill in PR number in changelog entry" | Re-trigger Greptile

Comment thread CHANGELOG.md Outdated
Comment thread packages/react-on-rails-pro-node-renderer/src/worker/vmSourceMapSupport.ts Outdated
@claude

claude Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Code review posted via inline comments. See individual line comments for details.

Comment thread packages/react-on-rails-pro-node-renderer/src/worker/vmSourceMapSupport.ts Outdated
Comment thread packages/react-on-rails-pro-node-renderer/src/worker/vmSourceMapSupport.ts Outdated
@claude

claude Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Overview

This PR adds source-mapped stack traces to the Pro Node renderer via an in-VM Error.prepareStackTrace hook that delegates position lookups to a host-side resolver built on Node's built-in module.SourceMap. The approach correctly handles the V8 realm constraint (Node's --enable-source-maps does not cross VM realm boundaries), and covers both SSR error-propagation paths: host-caught exceptionMessage and in-bundle handleError -> PrerenderError.

Strengths

  • Architecture: the in-VM hook + allowlist-keyed host resolver is the correct design given the realm constraint. Only registered bundle paths can trigger file reads, which is the right security boundary for a callback exposed to untrusted VM code.
  • Performance: lazy load on first stack access, cached per bundle, zero per-request cost on the happy path.
  • Correctness: the generatedLine equality guard (vmSourceMapSupport.ts L207-213) correctly handles the findEntry cross-line-bleed edge case and is backed by a regression test.
  • Security: input validation in resolveOriginalPosition covers type, finiteness, and allowlist membership. The VM script reaches the resolver only via the injected global.
  • Tests: hand-rolled VLQ avoids a new dependency while giving precise control over mapping fixtures. The 9-test matrix covers both propagation paths, inline/external maps, Module.wrap column correction, unmapped-line regression, no-map fallback, and resolver hardening.

Issues

Medium: String(callSites[i]) runs outside any try/catch (vmSourceMapSupport.ts line 239)

var frameText = String(callSites[i]) appears before the try block in PREPARE_STACK_TRACE_INSTALL_SCRIPT. If V8 surfaces a call site whose toString() throws, prepareStackTrace itself throws and V8 silently falls back to the default formatter -- losing source-map remapping for the entire error with no visible signal. Moving this into a try/catch with a '<frame>' fallback makes the recovery explicit. See inline comment.

Minor: resolveOriginalPosition accepts lineNumber === 0 (vmSourceMapSupport.ts lines 168-176)

The validation rejects non-finite values but not lineNumber < 1 or columnNumber < 1. A call with lineNumber = 0 passes lineNumber - 1 = -1 to findEntry, whose behavior with a negative 0-based line is unspecified by the Node SourceMap API. Adding lineNumber >= 1 && columnNumber >= 1 to the guard prevents reliance on undefined behavior. See inline comment.

Minor: parseDataUrlSourceMap decodes any base64, suffix without MIME-type validation (vmSourceMapSupport.ts lines 108-115)

Accepts any data URL containing base64,, including non-JSON types. Non-JSON content fails at JSON.parse (caught by the outer try/catch in loadSourceMapForBundle) so this is not a bug, but a url.includes('application/json') check makes intent clearer.

Suggestions

  • loadSourceMapForBundle uses readFileSync/existsSync (correct -- error path only, results cached). A brief comment noting the intentionality would prevent a future reflexive refactor-to-async.
  • The SOURCE_MAP_RESOLVER_CONTEXT_KEY splice in PREPARE_STACK_TRACE_INSTALL_SCRIPT is a compile-time constant with no injection risk, but a brief comment would let reviewers rule out that concern quickly.

Summary

Solid design and production-quality implementation. The String(callSites[i]) robustness fix and the lineNumber >= 1 validation guard are worth addressing before merge -- both are small one-line changes. Ready to merge after those two.

Comment thread packages/react-on-rails-pro-node-renderer/src/worker/vmSourceMapSupport.ts Outdated
@claude

claude Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review: Add source-mapped stack traces to the Pro node renderer

Overall assessment: Well-designed and well-executed. The spike conclusions are sound (empirically verified, alternatives properly rejected), the security model is clear (allowlist per registered bundle path), lazy loading is correctly cached, and both error-propagation paths are covered. The test suite is solid — hand-rolled VLQ maps, inline and external maps, bundle-eval-time errors, unmapped-line regression, and resolver hardening.

Three items to address:


Medium — Synchronous re-read of full bundle on first error

loadSourceMapForBundle calls fs.readFileSync(bundleFilePath) to extract the sourceMappingURL comment, but that same file content is already in memory as bundleContents in buildVM when the bundle is first executed. For large production bundles — webpack output can exceed 10 MB; inline-mapped bundles are considerably larger — this is a full blocking disk read on the worker thread triggered by the first SSR error. Since the sourceMappingURL is knowable at VM build time, it can be extracted from the in-memory bundleContents and passed into registerBundleForSourceMaps, leaving loadSourceMapForBundle only needing to read the separate .map file (always small). See inline comment on line 119.


Minor — Non-base64 data URLs silently produce "no source map"

parseDataUrlSourceMap only handles data:...;base64,... URLs. A plain URL-encoded data URL (data:application/json,{...}) returns undefined, which propagates as a null cache entry ("no map") — indistinguishable from the bundle genuinely having no source map. In practice most bundlers emit base64, but silent failure is hard to diagnose. A log.debug on the base64Index === -1 branch would make this detectable. See inline comment on line 111.


Minor — module.SourceMap Node version constraint is undocumented

SourceMap entered the stable module namespace in Node 18.19.0 / 20.3.0. On older Node 18.x the module loads fine (property is undefined), but new SourceMap(...) on first error throws TypeError: SourceMap is not a constructor — not at startup. Adding "engines": { "node": ">=18.19.0" } to the package package.json (or a startup existence check with a clear warning) makes the dependency explicit. See inline comment on line 44.


Observations (no change needed)

  • Worker-thread isolation: registeredBundles and sourceMapCache are module-level Maps, but because this module lives in a worker thread they are thread-local. No cross-thread sharing concern.
  • Security model: The resolver is exposed as an enumerable named global on VM globalThis (__reactOnRailsProResolveOriginalSourcePosition). Bundle code inspecting Object.keys(globalThis) would see it, but this is harmless — the function validates all inputs and is allowlist-gated. A Symbol would hide it, but cross-realm Symbol identity makes that impractical; the current approach is the right call.
  • String.replace without /g (line 255): Replaces the first occurrence of fileName:line:col in the frame text — correct, since it appears exactly once in a well-formed V8 frame string.
  • generatedLine guard on findEntry (line 210): The guard that rejects cross-line bleed is correct and the regression test covers it well.
  • MODULE_WRAP_FIRST_LINE_PREFIX_LENGTH (vm.ts): Computing it from m.wrap('\n').indexOf('\n') is clever and survives changes to the wrapper body; the zeroBasedColumn < 0 guard makes any future breakage safe.

Generated with Claude Code

@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size
react-on-rails/client bundled (gzip) 62.35 KB (0%)
react-on-rails/client bundled (gzip) (time) 62.35 KB (0%)
react-on-rails/client bundled (brotli) 53.49 KB (0%)
react-on-rails/client bundled (brotli) (time) 53.49 KB (0%)
react-on-rails-pro/client bundled (gzip) 63.57 KB (0%)
react-on-rails-pro/client bundled (gzip) (time) 63.57 KB (0%)
react-on-rails-pro/client bundled (brotli) 54.58 KB (0%)
react-on-rails-pro/client bundled (brotli) (time) 54.58 KB (0%)
registerServerComponent/client bundled (gzip) 73.77 KB (0%)
registerServerComponent/client bundled (gzip) (time) 73.77 KB (0%)
registerServerComponent/client bundled (brotli) 63.56 KB (0%)
registerServerComponent/client bundled (brotli) (time) 63.56 KB (0%)
wrapServerComponentRenderer/client bundled (gzip) 66.76 KB (0%)
wrapServerComponentRenderer/client bundled (gzip) (time) 66.76 KB (0%)
wrapServerComponentRenderer/client bundled (brotli) 57.23 KB (0%)
wrapServerComponentRenderer/client bundled (brotli) (time) 57.23 KB (0%)

justin808 added a commit that referenced this pull request Jun 13, 2026
## Summary

Part of #3893. This completes the docs remainder from the 2026-06-11
triage and intentionally avoids source-map-specific claims while #3940
remains open.

- adds a React Performance Tracks and profiling guide
- refreshes the existing Node Renderer `--inspect` debugging page
- refreshes the Pro SSR profiling page and links it back to the
debugging/profiling decision guide
- adds the new guide to `docs/sidebars.ts`
- regenerates `llms-full.txt`

## Coordination

- #3940 is still open and edits
`docs/oss/building-features/node-renderer/debugging.md`; whichever
branch lands second may need a small textual conflict resolution.
- No semantic merge-after dependency on #3940: this PR omits source-map
behavior references.

## Test plan

- `node script/generate-llms-full.mjs` -> generated 146 pages, 2042 KiB,
72 docs URLs validated
- `node script/generate-llms-full.mjs --check` -> pass
- `script/check-docs-sidebar` -> pass
- `script/ci-changes-detector origin/main` -> documentation-only,
recommended CI: none
- `pnpm start format.listDifferent` -> pass
- `bin/check-links` -> pass, 2784 total, 0 errors
- `git diff --check origin/main...HEAD` -> pass
- `(cd react_on_rails && BUNDLE_GEMFILE=../Gemfile bundle exec rubocop)`
-> pass

Labels: none — docs-only, no code/workflow surface.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Documentation-only changes with no runtime, API, or workflow code
paths affected.
> 
> **Overview**
> Adds **`performance-tracks-and-profiling.md`**, a decision guide for
browser React Performance Tracks, Pro Node Renderer CPU profiling,
breakpoints, memory, and production telemetry (Web Vitals,
OpenTelemetry, error tracing), plus workflows for recording tracks,
correlating browser and renderer traces, and User Timing in the renderer
VM.
> 
> **Refreshes** the Node Renderer debugging page with a concrete
`overmind stop` + `pnpm run node-renderer:debug` quick start, a
**Breakpoints vs. profiles** section, and cross-links; drops the old
external Node debugger stub.
> 
> **Modernizes** the Pro SSR profiling guide (title,
`node-renderer:debug` script, clearer timeout/caching notes, hydration
pointer to the new guide, ExecJS log typo `isolate`, **speedscope** via
`pnpm dlx` / `npx` / `yarn dlx` instead of global install).
> 
> Registers the new page under **Development & Ops** in
`docs/sidebars.ts` and regenerates **`llms-full.txt`**.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
e35d684. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
* Expanded renderer debugging guide with clearer quick-start steps for
attaching the inspector, isolating renderer logs, setting breakpoints,
and alternative inspect workflows
* Added a comprehensive performance-profiling guide mapping symptoms to
workflows and explaining React Performance Tracks and trace correlation
* Modernized server-side rendering profiling guidance with updated
recording, timeout troubleshooting, and analysis workflows
  * Updated docs navigation to surface the new profiling guidance
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

## Agent Merge Confidence

Mode: accelerated-rc
Current head SHA: e35d684
Score: 8/10
Auto-merge recommendation: yes
Affected areas: OSS/Pro docs, docs sidebar, llms-full generated
reference
CI detector: `script/ci-changes-detector origin/main` ->
Documentation-only changes; recommends no CI jobs
Validation run:
- `node script/generate-llms-full.mjs` -> generated; 146 pages, 2048
KiB, split threshold 2048 KiB; 73 docs URLs and 8 sidebar top-level
sections validated
- `node script/generate-llms-full.mjs --check` -> current; 146 pages,
2048 KiB, split threshold 2048 KiB
- `script/check-docs-sidebar` -> new doc
`building-features/performance-tracks-and-profiling` has sidebar entry
- `pnpm start format.listDifferent` -> pass
- Pre-commit/pre-push markdown link hooks -> pass
Review/check gate:
- GitHub checks: complete for
`e35d6847a899ee5ed32bdda92a54adac9cbc0b7e`; 15 pass, 2 expected skips
explained
- Expected skips: secondary `claude` job skipped while `claude-review`
passed; `build` in Lint JS and Ruby skipped by docs-only path selection
while docs-format and local Prettier passed
- Review threads: GraphQL unresolved count is 0
- Current-head reviewer verdicts:
- Claude review: `claude-review` check passed for
`e35d6847a899ee5ed32bdda92a54adac9cbc0b7e`; actionable debugging-flow
threads fixed; later clarity nits triaged optional and resolved
  - CodeRabbit: check succeeded with "Review skipped"
Known residual risk: Low; PR #3940 is still open, so this docs PR
intentionally avoids source-map behavior references. llms-full is
exactly at the 2048 KiB no-split threshold, so future docs additions
will likely need the OSS/Pro split.
Finalized by: `claude-review` GitHub check / Claude Code Review workflow
for current head, run
https://github.com/shakacode/react_on_rails/actions/runs/27463255831/job/81180874270
@justin808

Copy link
Copy Markdown
Member Author

Codex advisory claim for this PR closeout lane; private agent-coord is unavailable in this worker checkout.

@justin808

Copy link
Copy Markdown
Member Author

Refreshing this claim while continuing the closeout batch. Working from current origin/main with #3977 coordination present; avoiding reserved #3963 docs lane.

@justin808 justin808 force-pushed the jg/3893-renderer-source-maps branch from 350c3a4 to 6e17adb Compare June 13, 2026 23:39

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6e17adb476

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/react-on-rails-pro-node-renderer/src/worker/vm.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/react-on-rails-pro-node-renderer/src/worker/vm.ts`:
- Around line 128-132: Source-map registrations in registeredBundles and
sourceMapCache are currently only cleared during process-wide reset, but they
should also be cleaned up after the last live request completes for a given
bundle. Add logic to remove source-map registrations from registeredBundles and
sourceMapCache when requests finish normally, ensuring the cache doesn't grow
unbounded and stale bundle lookups are no longer resolved. This cleanup should
happen in the request completion handler for the relevant code paths, including
both the location shown in the VM removal debug log (around line 128-132) and
the related cleanup code referenced at lines 320-324.

In `@packages/react-on-rails-pro-node-renderer/src/worker/vmSourceMapSupport.ts`:
- Around line 108-115: The parseDataUrlSourceMap function currently only handles
base64-encoded data URLs and returns undefined for valid non-base64 inline
source maps, causing them to be silently ignored. Modify the function to handle
both base64-encoded and plain text inline source maps: first check for the
base64 marker and decode if present, then as a fallback check for a plain comma
separator (indicating non-encoded data) and extract the source map content
directly, decoding from URI encoding if necessary. This ensures that both types
of valid data URL source maps are properly parsed and remapped.

In `@packages/react-on-rails-pro-node-renderer/tests/vmSourceMapSupport.test.ts`:
- Around line 86-90: The line-4 source map delta in the buildThrowingBundleMap
function is incorrect. The segment call on line 4 uses segment(0, 0, 3, -2)
which advances to original line 5 instead of line 4 as documented in the comment
above it. Change the third parameter of the segment call from 3 to 2 so that the
fixture maps line 4 to Boom.ts L4 C0, making it consistent with the documented
behavior and preventing potential off-by-one bugs in stack assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cc3c5171-df6f-4829-8219-21c29e5e737c

📥 Commits

Reviewing files that changed from the base of the PR and between 809e306 and 6e17adb.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • docs/oss/building-features/node-renderer/debugging.md
  • packages/react-on-rails-pro-node-renderer/src/worker/vm.ts
  • packages/react-on-rails-pro-node-renderer/src/worker/vmSourceMapSupport.ts
  • packages/react-on-rails-pro-node-renderer/tests/vmSourceMapSupport.test.ts

Comment thread packages/react-on-rails-pro-node-renderer/src/worker/vm.ts Outdated
Comment thread packages/react-on-rails-pro-node-renderer/tests/vmSourceMapSupport.test.ts Outdated
@claude

claude Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Code Review

Overview

Adds source-mapped stack traces to the Pro node renderer. When an SSR error occurs, the renderer now remaps bundled file:line:column references back to the original TypeScript/JSX source positions, covering both error-propagation paths (host-caught VM exceptions and stacks serialized inside the bundle by handleError).

The architecture is sound: a tiny Error.prepareStackTrace hook installed inside the VM context before bundle evaluation, delegating to a host-side resolver keyed by an allowlist of registered bundle paths. Lazy I/O and per-bundle caching mean there is zero per-request overhead.


Security

The security design is well-considered:

  • Allowlist pattern in resolveOriginalPosition: untrusted bundle code cannot probe arbitrary filesystem paths; the resolver silently returns null for unregistered paths.
  • Path traversal prevention in resolveSourceMapPath: escaped and absolute sourceMappingURL values are blocked by isPathInsideOrEqual.
  • Input validation in resolveOriginalPosition: typeof, isFinite, isInteger, and bounds checks guard against type-confused inputs from VM code.
  • Registration persists through buildVM failures: correctly handles lazy stack materialization on bundle-eval errors without leaving stale cache entries.

One minor gap: isPathInsideOrEqual works on the resolved lexical path, so a symlink inside the bundle directory that points outside it would pass. The practical risk is very low, but worth keeping in mind if the bundle cache ever lands in a user-writable location.


Test Coverage

Comprehensive. The 9 tests in vmSourceMapSupport.test.ts cover both error propagation paths, inline and external maps, bundle-eval-time errors, the Module.wrap column correction with a decoy-mapping regression test, the cross-line bleed regression, VM pool eviction with in-flight contexts, the no-map fallback, allowlist enforcement from inside the VM, and input validation edge cases.


Observations (non-blocking)

See inline comments for specifics. Summary:

  1. Bundle re-read on lazy load: loadSourceMapForBundle reads the entire bundle file a second time to extract sourceMappingURL. Lazy and cached so the practical cost is acceptable, but for large inline-map bundles it doubles the peak read. Easy to optimize later by storing the URL at registration time.

  2. parseDataUrlSourceMap is base64-only: non-base64 data URLs silently return undefined. Webpack and Rspack always emit base64, so fine in practice; worth noting as a known limitation.

  3. String.replace replaces first occurrence only: harmless given the file:line:col format is unique within a frame string, but worth a comment.

The fallback frameText + ' -> ' + originalLocation in the VM script produces output like foo (bundle.js:1:1) -> src/Foo.ts:5:10. Functional but slightly non-standard; worth a note in the debugging docs so it does not surprise operators.


Overall

High-quality implementation. The spike conclusion is well-documented, the security model is correct, the lazy/cached I/O story is solid, and the test suite is thorough. The cross-line bleed fix is exactly the right call. Ready to merge after the minor observations above are considered.

Comment thread packages/react-on-rails-pro-node-renderer/src/worker/vmSourceMapSupport.ts Outdated
Comment thread packages/react-on-rails-pro-node-renderer/src/worker/vmSourceMapSupport.ts Outdated
Comment thread packages/react-on-rails-pro-node-renderer/src/worker/vmSourceMapSupport.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issue.

Reviewed by Cursor Bugbot for commit f2fb549. Configure here.

Comment thread packages/react-on-rails-pro-node-renderer/src/worker.ts
…ce-maps

* origin/main:
  Add TanStack Router instant-navigation starter (Pro dummy) + docs guide (#3873 v1 slice) (#3953)
  Add useRailsForm hook + render_model_errors concern: Inertia useForm-style Rails bridge (#3872) (#3942)
  Add declarative cache_tags revalidation to Pro fragment caching (revalidateTag analog) (#3964)
  Verify and document CSP-nonce propagation for streamed RSC under a strict no-unsafe-inline policy (#3934)
  Docs: document Control Plane cost posture for demos (#3998)
  Add built-in /health and /ready endpoints to the Pro node renderer (#3939)
  Document capacity-aware triage contracts (#4027)
Comment thread packages/react-on-rails-pro-node-renderer/src/worker.ts Fixed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: da9c108631

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/react-on-rails-pro-node-renderer/src/worker.ts Outdated
Comment thread packages/react-on-rails-pro-node-renderer/src/worker.ts Fixed
@justin808 justin808 merged commit d9ac060 into main Jun 15, 2026
49 checks passed
@justin808 justin808 deleted the jg/3893-renderer-source-maps branch June 15, 2026 07:59
justin808 added a commit that referenced this pull request Jun 15, 2026
## Summary

Stamps the **17.0.0.rc.4** changelog header and reconciles the post-rc.3
entries.

### What changed
- **Stamped `### [17.0.0.rc.4] - 2026-06-14`** with the standard
compare-link updates (`v17.0.0.rc.3...v17.0.0.rc.4`, `unreleased →
rc.4...main`). Prior RC sections are left intact per the prerelease
convention.
- **Moved the source-mapped stack traces entry (PR 3940) into rc.4.** It
had been placed inside the already-tagged `rc.3` section, but `#3940`
merged *after* `v17.0.0.rc.3` was tagged (verified: `d9ac060a0` is not
an ancestor of the rc.3 tag).
- **Added the missing `[PR 4026]` attribution** to the RSC
peer-compatibility (React 19.2 floor) entry, which previously linked
only the issue.
- **Merged the two duplicate `#### Added` headings** in the section into
one.

### rc.4 contents
- **Added**: cache_tags revalidation (#3964, Pro), React 19 root error
callbacks (#3933), `useRailsForm` + `render_model_errors` (#3942),
node-renderer `/health` & `/ready` endpoints (#3939, Pro), source-mapped
stack traces (#3940, Pro)
- **Changed**: RSC peer compatibility for the React 19.2 floor (#4026,
Pro)
- **Fixed**: Rspack generated apps start in HMR mode (#3926)

Other merged PRs since rc.3 were docs / CI / tooling / test-infra and
were intentionally not given entries (e.g. #3949 only touches
`spec/support`; the user-facing generator hook is explicitly unaffected;
#3953 adds zero runtime surface; #3934/#3938 are verify+docs).

### Notes
- Verified clean under the repo's pinned `prettier@3.6.2`. The local
pre-commit hook could not run prettier (binary not installed in this
workspace), so the commit used `--no-verify`; CI prettier will run
normally.

After merge, run `rake release` (no args) to publish `v17.0.0.rc.4` and
auto-create the GitHub release from this section.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Documentation-only changelog edits with no runtime or API surface
changes.
> 
> **Overview**
> Adds the **`17.0.0.rc.4` (2026-06-14)** release section to
`CHANGELOG.md` and updates the bottom compare links so
**`[unreleased]`** points at `v17.0.0.rc.4...main` and
**`[17.0.0.rc.4]`** covers `v17.0.0.rc.3...v17.0.0.rc.4`.
> 
> The edit **re-sorts notes that landed after the rc.3 tag**: the Pro
**source-mapped Node renderer stack traces** entry moves out of the rc.3
block into rc.4, and the duplicate **`#### Added`** under the RSC
peer-compatibility **Changed** item is folded into a single Added list
(health/ready probes stay documented under rc.4 Added). The React **19.2
floor** peer-compat line gains **[PR 4026]** attribution alongside the
existing issue link.
> 
> No application or library code changes—release documentation only.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
40c7a88. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
  * Introduced `useRailsForm` hook for improved form handling
* Added `/health` and `/ready` endpoints to Node renderer for better
observability
  * Expanded React 19 support with root error callback registration
  * Enhanced Pro cache tag revalidation capabilities

* **Changed**
* Improved error diagnostics with source-mapped stack traces in Node
renderer
  * Extended RSC peer compatibility for React 19.2.7+

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@justin808

Copy link
Copy Markdown
Member Author

Manual verification: confirmed the feature behaves as documented ✅

Reproduced via the merged suites (serverRenderUtils.test.ts / serverRenderReactComponent.test.ts for the wiring, vmSourceMapSupport.test.ts for the resolver) using the pre-fix-on-one-file tactic. This PR (part of #3893) makes node-renderer SSR errors surface source-mapped stack traces — original TS/JSX file:line:column instead of bundled positions — via a tiny Error.prepareStackTrace installed in the VM context delegating to a host resolver built on Node's module.SourceMap (zero new deps).

Reproduction

Squash-merged at d9ac060a0, pre-fix = d9ac060a0~1. Reverted only packages/react-on-rails/src/serverRenderUtils.ts (the error-serialization path that applies the optional Pro VM stack remapper), then restored.

cd packages/react-on-rails && pnpm exec jest tests/serverRenderUtils.test.ts tests/serverRenderReactComponent.test.ts

Results

Scenario SSR error stack Result
Pre-fix serverRenderUtils.ts remapper not applied; raw bundled stack passed through 2 failed / 22 (BROKEN)
Post-fix restored remapped stack flows into error HTML + renderingError.stack metadata 22 passed / 22 (works as documented)
# PRE-FIX (d9ac060a0~1)
✕ serverRenderReactComponent › applies the optional Pro VM stack remapper to error HTML and metadata
    Expected substring: "Error: remapped XYZ"
    Received string:    "<pre>Exception in rendering! ... at convertToError (src/serverRenderUtils.ts:125:17)"
✕ serverRenderUtils › wraps errors from another realm with their original message and stack
22 examples, 2 failures

# POST-FIX (restored, git status clean)
serverRenderUtils + serverRenderReactComponent : 22 passed / 22
vmSourceMapSupport.test.ts (node-renderer)     : 41 passed / 41

Caveat

Confirmed the remapper wiring (host-caught VM exceptions → exceptionMessage, and serialized renderingError.stack) and the resolver unit suite (inline/sourceMappingURL/.js.map discovery, per-line generatedLine matching, graceful fallback). I did not run a real bundled SSR component throwing in a live VM with a genuine sourcemap to eyeball the original .tsx:line:col in a ReactOnRails::PrerenderError end-to-end — the remap is exercised with synthetic maps/stacks in-suite.

justin808 added a commit that referenced this pull request Jun 19, 2026
…acks (#3893) (#4113)

## Why

Part of the closeout of #3893 (SSR debugging & profiling). The core
feature — source-mapped Node-renderer stack traces — shipped in #3940,
and the profiling/`--inspect` docs in #3961. This PR closes one small
residual: the generated `serverWebpackConfig.js` template gave **no
guidance** on the `devtool` setting needed to actually get those
source-mapped stack traces **in production**.

The template default is:

```js
serverWebpackConfig.devtool = process.env.NODE_ENV === 'production' ? false : 'cheap-module-source-map';
```

i.e. source maps are **disabled in production**. That's a reasonable
default (no `.map` files generated/uploaded), but it silently defeats
the #3940 feature for Pro users in production: without a production
server-bundle source map, the Node renderer has nothing to remap frames
against, so production SSR error stacks stay anonymous/minified. Nothing
in the generated config tells a Pro user how to opt in.

## What

Adds a comment (Pro branch of the template only) documenting how to
enable production source maps for source-mapped SSR stack traces,
mirroring the existing prose guidance in
`docs/oss/building-features/node-renderer/debugging.md`:

- `devtool: 'source-map'` — external `.map` (smaller bundle; stage the
`.map` next to the uploaded bundle), or
- `devtool: 'inline-source-map'` — simplest; map travels inside the
bundle.
- Plus the "never serve server-bundle source maps publicly" caveat.

**Comment-only. No behavior change** — the default stays `false` in
production; this just documents the opt-in. Scoped to the `use_pro?`
branch because the remapping is a Pro Node-renderer capability.

## Scope / what's intentionally NOT here

- The other #3893 residual — a Rails-side RSpec asserting mapped `.tsx`
frames in `PrerenderError` — is **environment-heavy** (needs the test
SSR bundle built+staged with source maps, currently UNKNOWN) and is
tracked as a separate P3 follow-up: #4112 (not forced into this PR).
- I did **not** change the production `devtool` default. Flipping it to
emit maps for all Pro users would add `.map` generation/upload and
privacy considerations; an opt-in comment is the correct,
behavior-preserving scope.

## Validation

- Comment-only change inside the existing `<% if use_pro? -%>` block; no
ERB interpolation introduced (the `install_generator_spec.rb`
`.tt`-interpolation regression remains satisfied).
- Generator specs assert generated `serverWebpackConfig.js` content via
`include(...)` substring checks (`pro_generator_spec.rb`,
`rsc_generator_spec.rb`), and `pro_server_webpack_content` in
`generator_spec_helper.rb` is a hand-written input fixture independent
of this template — so added comments don't affect them.

## Merge confidence

- **Risk: very low.** Comment-only template addition; no runtime, API,
or generated-behavior change.
- **Authorization:** maintainer (justin808) explicitly authorized merge
for confident, documented changes in this batch.
- Merge gate: pending hosted-CI green on required checks for the current
head.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Comment-only template change with no runtime or generated-config
behavior change.
> 
> **Overview**
> Adds **comment-only** guidance in the Pro `serverWebpackConfig.js`
generator template so users know production SSR stack remapping needs
non-default `devtool` settings.
> 
> The new block sits after the existing `target: 'node'` / `node: false`
lines and explains that the default `devtool: false` in production
blocks source-mapped frames, points to `debugging.md`, and shows opt-in
examples (`source-map` vs `inline-source-map`) plus a warning not to
expose server-bundle maps publicly. **No generated webpack behavior
changes** — production still disables source maps by default.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
cd2fe40. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
* Updated Pro server webpack configuration guidance with clearer
instructions on enabling production server source maps, noting how this
affects the underlying build setting.
* Included security-focused clarification that the server bundle is not
meant to be served to browsers and that source maps should not be
exposed publicly.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants