Skip to content

Conversation

@AbanoubGhadban
Copy link
Collaborator

@AbanoubGhadban AbanoubGhadban commented Oct 29, 2025

Summary

Remove this paragraph and provide a general description of the code changes in your pull
request... were there any bugs you had fixed? If so, mention them. If
these bugs have open GitHub issues, be sure to tag them here as well,
to keep the conversation linked together.

Pull Request checklist

Remove this line after checking all the items here. If the item is not applicable to the PR, both check it out and wrap it by ~.

  • Add/update test to cover these changes
  • Update documentation
  • Update CHANGELOG file

Add the CHANGELOG entry at the top of the file.

Other Information

Remove this paragraph and mention any other important and relevant information such as benchmarks.


This change is Reviewable

Summary by CodeRabbit

  • Changes

    • Removed a parameter from the React Server Component render interface.
  • Chores

    • Added development test dependencies for filesystem mocking.
  • Tests

    • Significantly expanded tests for concurrent streaming, chunk normalization, and parallel-render correctness.
    • Added supporting test utilities for stream reading, async queuing, chunk sanitization, and HTTP-based render requests.
    • Improved test-side Redis connection lifecycle handling.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 29, 2025

Walkthrough

Adds RSC/HTML streaming test infrastructure and concurrency tests, helper utilities (AsyncQueue, StreamReader, chunk-normalizers), a small devDependency for mock-fs, and removes reactClientManifestFileName from the RSCRenderParams type. No public runtime API changes.

Changes

Cohort / File(s) Change Summary
Package devDependencies
packages/react-on-rails-pro/package.json
Added devDependencies: mock-fs (^5.5.0) and @types/mock-fs (^4.13.4); minor JSON formatting (trailing comma).
Type definition
packages/react-on-rails/src/types/index.ts
Removed reactClientManifestFileName: string from exported RSCRenderParams interface.
Test infra & queue
packages/react-on-rails-pro/tests/AsyncQueue.ts, packages/react-on-rails-pro/tests/StreamReader.ts
New AsyncQueue<T> with enqueue/dequeue/end semantics; new StreamReader wrapping a Readable into async chunk consumption (nextChunk()).
Chunk normalization utils
packages/react-on-rails-pro/tests/utils/removeRSCChunkStack.ts, packages/react-on-rails-pro/tests/utils/removeRSCStackFromAllChunks.ts
Added helpers to strip stack traces from RSC chunk JSON lines for stable test comparisons.
RSC streaming tests (Pro)
packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx, packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
Added tests mocking a client manifest, registering RSC components using Suspense and AsyncQueue, validating single and high-concurrency RSC stream outputs, isolation, and known log-leak behavior.
Node-renderer tests & utils
react_on_rails_pro/packages/node-renderer/tests/concurrentHtmlStreaming.test.ts, react_on_rails_pro/packages/node-renderer/tests/httpRequestUtils.ts, react_on_rails_pro/packages/node-renderer/tests/htmlStreaming.test.js
Added concurrency-focused integration tests for HTML streaming; introduced httpRequestUtils.ts with SERVER_BUNDLE_TIMESTAMP, RSC_BUNDLE_TIMESTAMP, createForm, and makeRequest; replaced inline form builder in htmlStreaming.test.js with createForm.
Redis client lifecycle
react_on_rails_pro/spec/dummy/client/app/utils/redisReceiver.ts
Introduced connectionPromise to serialize Redis connections; reuse in-flight connect and improved close lifecycle.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Test
    participant ROR as ReactOnRails
    participant Component
    participant Queue as AsyncQueue
    participant Stream
    participant Reader as StreamReader

    Test->>ROR: serverRenderRSCReactComponent(props)
    ROR->>Component: mount (Suspense)
    Component->>Queue: await dequeue()
    Note right of Queue: Producer can enqueue() later
    Queue-->>Component: resolve value -> render fragment
    Component->>Stream: emit RSC chunk(s)
    Stream->>Reader: pipe chunks
    Reader-->>Test: nextChunk() -> chunk string
    loop concurrency
      Test->>Queue: enqueue(value) for many renders
      Reader-->>Test: expectNextChunk() validate normalized chunk
    end
    Component->>Queue: end()
    Reader-->>Test: throw on end-of-stream
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Inspect AsyncQueue promise lifecycle, listener teardown, and edge cases (enqueue after end, dequeue after end).
  • Verify StreamReader piping, TextDecoder usage, and correct end/destroy handling.
  • Review concurrent test orchestration for flakiness/race conditions and chunk normalization correctness.
  • Confirm no remaining callsites expect reactClientManifestFileName.

Possibly related issues

Possibly related PRs

Suggested reviewers

  • justin808
  • Judahmeek
  • alexeyr-ci

Poem

🐇 Hopping through queues with a twitchy ear,

Chunks of stream arrive, tidy and clear,
Readers wait patient, tests clap their paws,
Parallel renders hum without claws,
A rabbit cheers the async frontier. ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Add Concurrency Tests" clearly and directly relates to the primary changes in the pull request. The changeset includes multiple new test files focused on concurrent rendering scenarios (serverRenderRSCReactComponent.rsc.test.tsx, concurrentRSCPayloadGeneration.rsc.test.tsx, and concurrentHtmlStreaming.test.ts), along with supporting utility classes and functions (AsyncQueue, StreamReader, etc.) to enable these tests. While the PR also includes a type change (removing reactClientManifestFileName from RSCRenderParams) and infrastructure updates, the dominant focus of the changeset is clearly on adding the concurrency testing infrastructure. The title is concise, specific, and accurately captures the main purpose without unnecessary verbosity.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch abanoubghadban/add-concurrency-tests

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.

@claude
Copy link

claude bot commented Oct 29, 2025

Pull Request Review

Thank you for submitting this PR! I've reviewed the changes and have identified several critical issues that need to be addressed before this can be merged.


Critical Issues ❌

1. Breaking Change: Removal of handleError import

Location: packages/react-on-rails-pro/src/ReactOnRailsRSC.ts:88

- const errorHtml = handleError({ e: error, name: options.name, serverSide: true });
+ const errorHtml = JSON.stringify({ e: error, name: options.name, serverSide: true });

Issues:

  • This is a breaking change that fundamentally alters error handling behavior
  • The original handleError function returns formatted HTML with proper error messages, stack traces, and helpful debugging information
  • JSON.stringify on an Error object will only serialize enumerable properties, losing the stack trace and potentially other critical debugging info
  • This change breaks the existing error display contract and will make debugging much harder for users
  • No explanation provided for why this change was made
  • This appears unrelated to "Add Concurrency Tests" title

Recommendation: Revert this change unless there's a specific technical reason (which should be documented in the PR description).


2. Incomplete Test Implementation

Location: packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.test.tsx

Issues:

  • Test name is 'eeee' (line 17) - this is clearly a placeholder
  • Test has no assertions on the actual function behavior
  • Test only checks that the mocked file system works, not that serverRenderRSCReactComponent works correctly
  • Missing critical test coverage:
    • No validation of render output
    • No error handling scenarios
    • No verification of streaming behavior
    • No validation of the removed handleError path
  • The PR title says "Add Concurrency Tests" but this test has nothing to do with concurrency
  • Mock is declared at module level (line 9-11) which could affect other tests

Code Quality Issues:

// Line 14: Missing semicolon (against project style)
})

// Line 20: Type assertion defeats the purpose of type checking
railsContext: {} as unknown as RailsContextWithServerStreamingCapabilities

// Line 27: Function call with no assertion of results
ReactOnRails.serverRenderRSCReactComponent({...})

3. Missing Test Coverage for Changed Code

The PR modifies error handling in ReactOnRailsRSC.ts but the test file doesn't test:

  • Error scenarios
  • The new JSON.stringify error handling path
  • Streaming behavior
  • Component rendering correctness

Code Quality Issues ⚠️

4. Jest Configuration Change Lacks Documentation

Location: packages/react-on-rails-pro/jest.config.js:35-37

testEnvironmentOptions: {
  customExportConditions: process.env.NODE_CONDITIONS?.split(','),
}

Issues:

  • No comment explaining why this is needed
  • No documentation of what NODE_CONDITIONS should contain
  • Not mentioned in PR description
  • Unclear how this relates to "concurrency tests"

5. New Dependencies

Location: packages/react-on-rails-pro/package.json

Added mock-fs and @types/mock-fs as dev dependencies.

Concerns:

  • mock-fs has known issues with Jest and modern Node.js versions
  • Consider using Jest's built-in mocking or memfs instead
  • No justification for why filesystem mocking is needed for RSC testing

Missing Items 📝

Based on the project's CLAUDE.md and PR template:

  • PR description is incomplete - still contains template text
  • CHANGELOG not updated - required per project guidelines
  • Test coverage inadequate - tests don't actually verify behavior
  • Documentation missing - no explanation of changes
  • Linting concerns - missing semicolon in test file (line 14)
  • Title mismatch - "Add Concurrency Tests" doesn't match actual changes

Recommendations 💡

Immediate Actions Required:

  1. Revert the handleError removal or provide strong justification with proper alternative implementation
  2. Complete the test implementation:
    • Give it a descriptive name
    • Add actual assertions
    • Test error scenarios
    • Test streaming output
    • Test concurrency if that's the goal
  3. Update PR description with:
    • What problem this solves
    • Why handleError was removed
    • What the jest config change enables
    • How this relates to concurrency
  4. Fix the PR title to accurately reflect changes
  5. Update CHANGELOG as required
  6. Add code comments explaining the jest config change

Suggested Test Structure:

describe('serverRenderRSCReactComponent', () => {
  it('should render a basic RSC component successfully', async () => {
    // Test basic rendering with assertions
  });
  
  it('should handle errors gracefully', async () => {
    // Test error scenarios
  });
  
  it('should stream component output correctly', async () => {
    // Test streaming behavior
  });
  
  // If testing concurrency:
  it('should handle concurrent render requests safely', async () => {
    // Test concurrent behavior
  });
});

Security Considerations 🔒

  • No security concerns identified in the current changes
  • Error handling change could potentially leak sensitive info if errors contain secrets (though this applies to both old and new approaches)

Performance Considerations ⚡

  • JSON.stringify on Error objects may be slightly faster than handleError, but loses critical debugging information
  • mock-fs can slow down test execution; consider alternatives

Please address these issues and update the PR. I'm happy to review again once changes are made! Let me know if you have questions about any of these points.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/react-on-rails-pro/jest.config.js (1)

35-37: Document NODE_CONDITIONS environment variable.

The NODE_CONDITIONS environment variable is used to configure custom export conditions but is not documented. This makes it unclear:

  • When and how developers should set this variable
  • What valid values are expected (e.g., react-server, node, etc.)
  • How this affects test execution

Add a comment explaining the usage:

   rootDir: '.',

+  // Custom export conditions for testing React Server Components
+  // Set NODE_CONDITIONS env var to control module resolution (e.g., NODE_CONDITIONS=react-server,node)
   testEnvironmentOptions: {
     customExportConditions: process.env.NODE_CONDITIONS?.split(','),
   },
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e02c7a and ef3ce5f.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (4)
  • packages/react-on-rails-pro/jest.config.js (1 hunks)
  • packages/react-on-rails-pro/package.json (1 hunks)
  • packages/react-on-rails-pro/src/ReactOnRailsRSC.ts (1 hunks)
  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.test.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,jsx,ts,tsx,css,scss,json,yml,yaml,md}

📄 CodeRabbit inference engine (CLAUDE.md)

Prettier is the sole authority for formatting all non-Ruby files; never manually format them

Files:

  • packages/react-on-rails-pro/jest.config.js
  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.test.tsx
  • packages/react-on-rails-pro/package.json
  • packages/react-on-rails-pro/src/ReactOnRailsRSC.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint for JS/TS code (lint via rake lint or yarn lint)

Files:

  • packages/react-on-rails-pro/jest.config.js
  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.test.tsx
  • packages/react-on-rails-pro/src/ReactOnRailsRSC.ts
🧠 Learnings (16)
📓 Common learnings
Learnt from: alexeyr-ci
PR: shakacode/react_on_rails#1644
File: node_package/src/ReactOnRailsRSC.ts:87-87
Timestamp: 2024-12-12T13:07:09.929Z
Learning: When handling errors in 'node_package/src/ReactOnRailsRSC.ts', include the error stack in error messages in development and test environments to aid debugging.
Learnt from: justin808
PR: shakacode/react_on_rails#1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Learnt from: alexeyr-ci2
PR: shakacode/react_on_rails#1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1696
File: node_package/src/RSCPayloadGenerator.ts:124-168
Timestamp: 2025-06-09T07:58:02.646Z
Learning: In React Server Components (RSC) implementations, explicit error handling in RSC payload generation streams (like in RSCPayloadGenerator.ts) is not needed because errors will propagate to client components that fetch the RSC payloads (such as RSCRoute components). React's built-in error handling mechanisms and ErrorBoundary components are sufficient for handling these errors at the component level, which allows for better user experience with appropriate error UI fallbacks.
Learnt from: theforestvn88
PR: shakacode/react_on_rails#1620
File: spec/dummy/client/app/startup/HelloTurboStream.jsx:3-3
Timestamp: 2024-07-27T10:08:35.868Z
Learning: The `RailsContext` import in `spec/dummy/client/app/startup/HelloTurboStream.jsx` is used later in the project, as clarified by the user theforestvn88.
Learnt from: theforestvn88
PR: shakacode/react_on_rails#1620
File: spec/dummy/client/app/startup/HelloTurboStream.jsx:3-3
Timestamp: 2024-10-08T20:53:47.076Z
Learning: The `RailsContext` import in `spec/dummy/client/app/startup/HelloTurboStream.jsx` is used later in the project, as clarified by the user theforestvn88.
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation is handled in deeper level calls of the React on Rails Pro codebase, so it doesn't need to be validated again in the `rsc_payload_react_component` helper method.
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1745
File: node_package/src/RSCRequestTracker.ts:8-14
Timestamp: 2025-07-08T05:57:29.630Z
Learning: The global `generateRSCPayload` function in React on Rails Pro (RORP) is provided by the framework during rendering requests, not implemented in application code. The `declare global` statements are used to document the expected interface that RORP will inject at runtime.
Learnt from: Romex91
PR: shakacode/react_on_rails#1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
📚 Learning: 2024-12-12T13:07:09.929Z
Learnt from: alexeyr-ci
PR: shakacode/react_on_rails#1644
File: node_package/src/ReactOnRailsRSC.ts:87-87
Timestamp: 2024-12-12T13:07:09.929Z
Learning: When handling errors in 'node_package/src/ReactOnRailsRSC.ts', include the error stack in error messages in development and test environments to aid debugging.

Applied to files:

  • packages/react-on-rails-pro/jest.config.js
  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.test.tsx
  • packages/react-on-rails-pro/src/ReactOnRailsRSC.ts
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
PR: shakacode/react_on_rails#1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.

Applied to files:

  • packages/react-on-rails-pro/jest.config.js
  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.test.tsx
  • packages/react-on-rails-pro/src/ReactOnRailsRSC.ts
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.test.tsx
📚 Learning: 2025-02-13T19:09:15.991Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: node_package/src/RSCWebpackLoader.ts:0-0
Timestamp: 2025-02-13T19:09:15.991Z
Learning: In React Server Components webpack loader, using `new Function('return import("react-server-dom-webpack/node-loader")')()` is necessary as a workaround to bypass TypeScript compilation issues with direct dynamic imports.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.test.tsx
  • packages/react-on-rails-pro/src/ReactOnRailsRSC.ts
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation is handled in deeper level calls of the React on Rails Pro codebase, so it doesn't need to be validated again in the `rsc_payload_react_component` helper method.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.test.tsx
📚 Learning: 2025-06-09T07:58:02.646Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1696
File: node_package/src/RSCPayloadGenerator.ts:124-168
Timestamp: 2025-06-09T07:58:02.646Z
Learning: In React Server Components (RSC) implementations, explicit error handling in RSC payload generation streams (like in RSCPayloadGenerator.ts) is not needed because errors will propagate to client components that fetch the RSC payloads (such as RSCRoute components). React's built-in error handling mechanisms and ErrorBoundary components are sufficient for handling these errors at the component level, which allows for better user experience with appropriate error UI fallbacks.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.test.tsx
  • packages/react-on-rails-pro/src/ReactOnRailsRSC.ts
📚 Learning: 2024-07-27T10:08:35.868Z
Learnt from: theforestvn88
PR: shakacode/react_on_rails#1620
File: spec/dummy/client/app/startup/HelloTurboStream.jsx:3-3
Timestamp: 2024-07-27T10:08:35.868Z
Learning: The `RailsContext` import in `spec/dummy/client/app/startup/HelloTurboStream.jsx` is used later in the project, as clarified by the user theforestvn88.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.test.tsx
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.test.tsx
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
PR: shakacode/react_on_rails#1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.test.tsx
  • packages/react-on-rails-pro/package.json
  • packages/react-on-rails-pro/src/ReactOnRailsRSC.ts
📚 Learning: 2025-01-23T18:20:45.824Z
Learnt from: alexeyr-ci
PR: shakacode/react_on_rails#1687
File: spec/dummy/package.json:0-0
Timestamp: 2025-01-23T18:20:45.824Z
Learning: When adding or updating dependencies in spec/dummy/package.json, maintain version consistency with other package.json files in the codebase to avoid potential version conflicts.

Applied to files:

  • packages/react-on-rails-pro/package.json
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
PR: shakacode/react_on_rails#1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.

Applied to files:

  • packages/react-on-rails-pro/src/ReactOnRailsRSC.ts
📚 Learning: 2025-07-08T05:57:29.630Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1745
File: node_package/src/RSCRequestTracker.ts:8-14
Timestamp: 2025-07-08T05:57:29.630Z
Learning: The global `generateRSCPayload` function in React on Rails Pro (RORP) is provided by the framework during rendering requests, not implemented in application code. The `declare global` statements are used to document the expected interface that RORP will inject at runtime.

Applied to files:

  • packages/react-on-rails-pro/src/ReactOnRailsRSC.ts
📚 Learning: 2025-02-13T16:50:47.848Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: node_package/src/clientStartup.ts:18-21
Timestamp: 2025-02-13T16:50:47.848Z
Learning: In the react_on_rails module, the `reactOnRailsPageUnloaded` function in clientStartup.ts is intentionally kept private as it's only used internally as a callback for `onPageUnloaded`.

Applied to files:

  • packages/react-on-rails-pro/src/ReactOnRailsRSC.ts
📚 Learning: 2025-04-09T12:56:10.756Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1696
File: node_package/src/RSCPayloadContainer.ts:0-0
Timestamp: 2025-04-09T12:56:10.756Z
Learning: In the react_on_rails codebase, RSC payloads are already stringified using `JSON.stringify()` before being processed by the `escapeScript` function, which handles escaping of special characters. The function only needs to handle specific HTML markers like comments and closing script tags.

Applied to files:

  • packages/react-on-rails-pro/src/ReactOnRailsRSC.ts
📚 Learning: 2025-10-23T17:22:01.074Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.

Applied to files:

  • packages/react-on-rails-pro/src/ReactOnRailsRSC.ts
🧬 Code graph analysis (2)
packages/react-on-rails-pro/jest.config.js (1)
react_on_rails_pro/spec/dummy/client/node-renderer.js (1)
  • process (5-5)
packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.test.tsx (1)
packages/react-on-rails/src/types/index.ts (1)
  • RailsContextWithServerStreamingCapabilities (65-68)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: dummy-app-integration-tests (3.4, 22)
  • GitHub Check: build
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: rspec-package-tests (3.2, latest)
  • GitHub Check: rspec-package-tests (3.4, minimum)
  • GitHub Check: rspec-package-tests (3.4, latest)
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: rspec-package-tests (3.2, minimum)
  • GitHub Check: lint-js-and-ruby
🔇 Additional comments (3)
packages/react-on-rails-pro/package.json (1)

77-80: LGTM!

The addition of mock-fs and @types/mock-fs as devDependencies is appropriate for supporting filesystem mocking in tests. Version ranges using caret (^) are standard for devDependencies.

packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.test.tsx (2)

1-27: Verify concurrency testing for PR alignment.

The PR title is "Add Concurrency Tests," but this test file doesn't contain any concurrency testing scenarios. Tests should verify:

  • Concurrent calls to serverRenderRSCReactComponent
  • Race conditions in manifest loading
  • Thread safety of the serverRendererPromise singleton (line 35 in ReactOnRailsRSC.ts)

Please confirm whether additional concurrency tests exist elsewhere or if this file should be extended to include them.


15-15: Add missing semicolon.

The afterAll callback is missing a semicolon, which is inconsistent with the coding style used elsewhere in the file.

Apply this diff:

   mock.restore();
-})
+});

Likely an incorrect or invalid review comment.

const error = convertToError(e);
reportError(error);
const errorHtml = handleError({ e: error, name: options.name, serverSide: true });
const errorHtml = JSON.stringify({ e: error, name: options.name, serverSide: true });
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical issues with error serialization.

This line has several problems:

  1. Variable name is misleading: errorHtml suggests HTML content, but it now contains JSON.
  2. Error object won't serialize properly: JSON.stringify() doesn't capture Error properties like message, stack, or custom properties. You'll get {} or minimal info.
  3. Missing stack trace: Based on learnings, error stack should be included in development and test environments to aid debugging.
  4. Unclear property name: The property e is not descriptive.

Apply this diff to fix the serialization and naming:

-    const errorHtml = JSON.stringify({ e: error, name: options.name, serverSide: true });
-    writeChunk(errorHtml);
+    const errorJson = JSON.stringify({ 
+      error: {
+        message: error.message,
+        stack: error.stack,
+        name: error.name,
+      },
+      componentName: options.name, 
+      serverSide: true 
+    });
+    writeChunk(errorJson);

Based on learnings

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/react-on-rails-pro/src/ReactOnRailsRSC.ts around line 88, the
current JSON serialization uses a misleading variable name and will not capture
Error details; replace it with a clearly named variable (e.g., errorPayload)
that constructs a plain object containing error: { message: error.message },
name: options.name, serverSide: true, and include error.stack when
process.env.NODE_ENV is "development" or "test"; then JSON.stringify that
payload so consumers receive message, optional stack (only in dev/test), and
descriptive property names instead of an empty or opaque object.

Comment on lines 17 to 27
test('eeee', () => {
expect(fs.readFileSync('./server/react-client-manifest.json').toString()).toEqual('{}');
ReactOnRails.serverRenderRSCReactComponent({
railsContext: {} as unknown as RailsContextWithServerStreamingCapabilities,
name: 'Component1',
reactClientManifestFileName: 'react-client-manifest.json',
renderingReturnsPromises: true,
throwJsErrors: true,
domNodeId: 'dom-id',
})
})
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Test is incomplete and doesn't match PR objectives.

Several critical issues with this test:

  1. Test name 'eeee' is a placeholder - use a descriptive name like 'should render RSC component with mocked filesystem'
  2. No assertions on rendering result - the test calls serverRenderRSCReactComponent but doesn't verify the output or behavior
  3. Empty railsContext - casting {} to RailsContextWithServerStreamingCapabilities bypasses type safety. The real type requires properties like getRSCPayloadStream and addPostSSRHook (see type definition), which will cause runtime errors
  4. PR title is "Add Concurrency Tests" but this doesn't test any concurrency scenarios
  5. Missing error handling tests - given the related change in ReactOnRailsRSC.ts that modifies error serialization, this test should verify error scenarios

Consider this structure:

-test('eeee', () => {
-  expect(fs.readFileSync('./server/react-client-manifest.json').toString()).toEqual('{}');
-  ReactOnRails.serverRenderRSCReactComponent({
-    railsContext: {} as unknown as RailsContextWithServerStreamingCapabilities,
+test('should render RSC component with mocked filesystem', async () => {
+  const mockRailsContext: RailsContextWithServerStreamingCapabilities = {
+    getRSCPayloadStream: jest.fn(),
+    addPostSSRHook: jest.fn(),
+    reactClientManifestFileName: 'server/react-client-manifest.json',
+    // ... other required properties
+  };
+
+  const result = ReactOnRails.serverRenderRSCReactComponent({
+    railsContext: mockRailsContext,
     name: 'Component1',
-    reactClientManifestFileName: 'react-client-manifest.json',
+    reactClientManifestFileName: 'server/react-client-manifest.json',
     renderingReturnsPromises: true,
     throwJsErrors: true,
     domNodeId: 'dom-id',
-  })
-})
+  });
+
+  // Add assertions on result
+  expect(result).toBeDefined();
+  // Verify the stream output, error handling, etc.
+});

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.test.tsx
around lines 17 to 27, the test uses a placeholder name, lacks assertions,
improperly casts an empty object to RailsContextWithServerStreamingCapabilities,
doesn't exercise concurrency, and omits error-case checks; fix by renaming the
test to a descriptive title (e.g., "should render RSC component with mocked
filesystem"), mock fs.readFileSync/react-client-manifest.json properly, provide
a realistic railsContext mock implementing getRSCPayloadStream and
addPostSSRHook (and any streaming behavior needed), await and assert on the
serverRenderRSCReactComponent return or side-effects (including verifying output
and that react-client-manifest was used), add a separate concurrent invocation
test (e.g., Promise.all of multiple renders) to exercise concurrency, and add
tests that trigger and assert error serialization/handling paths introduced in
ReactOnRailsRSC.ts.

@claude
Copy link

claude bot commented Oct 30, 2025

Comprehensive Pull Request Review

Thank you for submitting this PR! I've conducted a thorough review of the changes and have identified several critical issues that need to be addressed before this can be merged.


🚨 Critical Issues

1. Breaking Change: Removal of handleError Function

Location: packages/react-on-rails-pro/src/ReactOnRailsRSC.ts:89

- const errorHtml = handleError({ e: error, name: options.name, serverSide: true });
+ const errorHtml = JSON.stringify({ e: error, name: options.name, serverSide: true });

Critical Issues:

  • This is a breaking change that fundamentally alters error handling behavior
  • The original handleError function (from handleErrorRSC.ts) returns a properly formatted React Server Component stream with error messages
  • JSON.stringify on an Error object loses the stack trace - Error objects only have enumerable properties name and message, the stack property is non-enumerable
  • The stringified JSON is being passed to pipeToTransform which expects a stream, not a string
  • This will likely cause runtime errors or broken error display
  • No explanation in the PR description for why this change was made
  • This change appears completely unrelated to the PR title "Add Concurrency Tests"

Impact: This will break error reporting for all RSC rendering failures, making debugging significantly harder for developers.

Recommendation: Immediately revert this change. If there's a specific reason for removing handleError, it needs to be:

  1. Documented in the PR description
  2. Properly implemented with a working alternative
  3. Tested with error scenarios

2. Incomplete and Non-Functional Test

Location: packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.test.tsx

Critical Issues:

Line 17: Test name is 'eeee' - clearly a placeholder

test('eeee', () => {

Line 18-27: Test has zero assertions about the actual function behavior:

expect(fs.readFileSync('./server/react-client-manifest.json').toString()).toEqual('{}');
ReactOnRails.serverRenderRSCReactComponent({
  // ... just calls the function, no assertion on result
})

Line 14: Missing semicolon (violates project style guidelines)

})  // ← Missing semicolon

Line 20: Empty object type assertion defeats type safety:

railsContext: {} as unknown as RailsContextWithServerStreamingCapabilities

Line 9-11: Mock declared at module level could leak between tests:

mock({
  'server/react-client-manifest.json': '{}'
});
// This affects all tests if not properly isolated

Missing Coverage:

  • No validation of render output
  • No error handling scenarios (especially critical given the handleError removal!)
  • No verification of streaming behavior
  • No concurrency testing (despite PR title)
  • No assertions on component rendering correctness
  • No cleanup between test cases

Recommendation: Complete test implementation with:

  • Descriptive test name
  • Proper test isolation (move mock into beforeEach)
  • Multiple test cases covering success and error scenarios
  • Actual assertions on output
  • Error handling tests to validate the removed handleError path
  • If testing concurrency, add concurrent request tests

3. Title and Description Mismatch

Issues:

  • PR title: "Add Concurrency Tests"
  • Actual changes: Jest config update, mock-fs dependency, incomplete RSC test, breaking error handling change
  • PR description still contains template text
  • No mention of why handleError was removed
  • No explanation of what concurrency issue this addresses

Recommendation: Update title to reflect actual changes, e.g., "Add RSC server rendering tests and update Jest configuration" or provide missing concurrency tests.


⚠️ Code Quality Issues

4. Undocumented Jest Configuration Change

Location: packages/react-on-rails-pro/jest.config.js:35-37

testEnvironmentOptions: {
  customExportConditions: process.env.NODE_CONDITIONS?.split(','),
}

Issues:

  • No comment explaining why this is needed
  • No documentation of what NODE_CONDITIONS should contain
  • No examples of usage
  • Not mentioned in PR description
  • Unclear relationship to stated goal

Recommendation: Add inline comment explaining:

// Custom export conditions for React Server Components testing
// Set NODE_CONDITIONS env var (e.g., NODE_CONDITIONS=react-server,node)
testEnvironmentOptions: {
  customExportConditions: process.env.NODE_CONDITIONS?.split(','),
}

5. Dependency Concerns: mock-fs

Location: packages/react-on-rails-pro/package.json:76-79

Issues:

  • mock-fs has known compatibility issues with Jest and modern Node.js
  • Can cause test suite slowdowns
  • May have race conditions with parallel test execution
  • Consider alternatives like Jest's built-in mocking or memfs
  • No justification for why filesystem mocking is needed for RSC component testing

Recommendation: Consider alternatives or document why mock-fs is specifically needed.


📋 Missing Requirements (per CLAUDE.md)

Based on the repository's CLAUDE.md guidelines:

  • PR Description: Still contains template placeholder text
  • CHANGELOG: Not updated (required per project guidelines)
  • Test Coverage: Tests don't actually verify behavior
  • Documentation: No explanation of changes or rationale
  • Code Quality: Missing semicolon (line 14), placeholder test name
  • Linting: Must run bundle exec rubocop before commit (critical requirement)

🔒 Security Considerations

  • No direct security vulnerabilities identified
  • The error handling change could potentially expose sensitive information if errors contain secrets (though this applies to both approaches)
  • Type assertion {} as unknown as RailsContextWithServerStreamingCapabilities bypasses type safety and could hide runtime errors

⚡ Performance Considerations

  • JSON.stringify is faster than handleError but loses critical debugging information
  • mock-fs can slow down test execution significantly
  • Module-level mock may cause issues with parallel test execution

✅ Recommended Action Items

Immediate (Blocking Issues):

  1. Revert the handleError removal in ReactOnRailsRSC.ts or provide proper replacement
  2. Complete the test file:
    • Replace 'eeee' with descriptive test name
    • Add actual assertions
    • Test error scenarios
    • Add missing semicolon
    • Move mock to beforeEach for proper isolation
  3. Update PR title to match actual changes
  4. Complete PR description:
    • Remove template text
    • Explain rationale for all changes
    • Document the handleError removal reason
    • Explain Jest config change
  5. Update CHANGELOG (required per CLAUDE.md)
  6. Run linting: bundle exec rubocop and yarn run lint

Suggested Test Structure:

describe('serverRenderRSCReactComponent', () => {
  let mockManifest: Record<string, unknown>;

  beforeEach(() => {
    mockManifest = {};
    mock({
      'server/react-client-manifest.json': JSON.stringify(mockManifest)
    });
  });

  afterEach(() => {
    mock.restore();
  });

  it('should render a basic RSC component successfully', () => {
    const result = ReactOnRails.serverRenderRSCReactComponent({
      railsContext: createMockRailsContext(),
      name: 'Component1',
      reactClientManifestFileName: 'react-client-manifest.json',
      renderingReturnsPromises: true,
      throwJsErrors: false,
      domNodeId: 'dom-id',
    });
    
    expect(result).toBeDefined();
    expect(result.hasErrors).toBe(false);
    // Add more assertions
  });
  
  it('should handle rendering errors gracefully', () => {
    // Test error scenarios - CRITICAL given handleError removal
  });
  
  it('should handle concurrent render requests safely', async () => {
    // Test concurrency if that's the goal
  });
});

📊 Review Summary

Overall Assessment:Changes Requested

This PR requires significant work before it can be merged:

  • Contains a breaking change that breaks error handling
  • Test implementation is incomplete with no assertions
  • Title and description don't match actual changes
  • Missing required documentation and CHANGELOG updates

Positive Notes:

  • Jest configuration addition could be useful (needs documentation)
  • Attempting to add RSC test coverage is valuable

Please address the critical issues above, particularly the handleError removal and incomplete test implementation. I'm happy to review again once these are resolved!

Feel free to ask questions if anything is unclear. 🙂

@claude
Copy link

claude bot commented Oct 30, 2025

Code Review for PR #1890: Add Concurrency Tests

Thank you for working on this PR! I've reviewed the changes and have several important concerns and suggestions.

🚨 Critical Issues

1. Breaking Change to Error Handling (packages/react-on-rails-pro/src/ReactOnRailsRSC.ts:88-89)

The change from using handleError() to JSON.stringify() is a breaking change that needs careful consideration:

// Before
const errorHtml = handleError({ e: error, name: options.name, serverSide: true });

// After  
const errorHtml = JSON.stringify({ e: error, name: options.name, serverSide: true });

Issues:

  • This changes the error response format from formatted HTML to raw JSON
  • The variable name errorHtml is now misleading (it's JSON, not HTML)
  • Client code expecting HTML error responses will break
  • Error objects don't serialize well to JSON (stack traces, custom properties, circular references)
  • This appears unrelated to "Add Concurrency Tests" title

Questions:

  • What is the intended purpose of this change?
  • Is this related to concurrency testing, or is it a separate refactor?
  • Has the impact on existing error handling been evaluated?

2. Incomplete Test Implementation (packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.test.tsx)

The test file has several issues:

a) Placeholder test name:

test('eeee', () => {  // ❌ Not descriptive

Should be: test('renders RSC component with server streaming', () => {

b) No assertions on the actual rendering:

ReactOnRails.serverRenderRSCReactComponent({...})  // ❌ Result not captured or tested

c) Missing semicolons (lines 10, 14):

mock({
  'server/react-client-manifest.json': '{}'
});  // ❌ Missing semicolon

afterAll(() => {
  mock.restore();
})  // ❌ Missing semicolon

d) Mock setup outside test:

  • The mock() call at line 8 runs globally, which can cause test isolation issues
  • Should be moved inside the test or into a beforeEach hook

e) No actual concurrency testing:

  • Despite the PR title "Add Concurrency Tests", this test doesn't test any concurrent behavior
  • No multiple simultaneous renders, no race conditions, no parallel execution

3. mock-fs Compatibility Issues

The mock-fs library (v5.5.0) has known issues:

  • Not compatible with Node.js 20+ (this project uses Node 20.17.16)
  • Deprecated and not actively maintained
  • Can cause issues with native modules and ESM imports

Recommendation: Use memfs or Jest's built-in jest.mock('fs') instead.

⚠️ Medium Priority Issues

4. Missing PR Checklist Items

The PR description shows unchecked items:

  • Add/update test to cover these changes
  • Update documentation
  • Update CHANGELOG file

These should be completed before merging.

5. Jest Configuration Change (packages/react-on-rails-pro/jest.config.js:34-37)

testEnvironmentOptions: {
  customExportConditions: process.env.NODE_CONDITIONS?.split(','),
}

Issues:

  • No explanation for why this is needed
  • Could break tests if NODE_CONDITIONS is not set (will be undefined)
  • Not documented in the PR description

Suggestion: Add comments explaining the purpose and set a default value.

6. Inconsistent File Paths

The test mocks 'server/react-client-manifest.json' but references './server/react-client-manifest.json' in the assertion. While both may work, consistency is better.

📋 Required Before Merging

According to CLAUDE.md requirements:

  1. Run bundle exec rubocop - All violations must be fixed
  2. Ensure trailing newlines - All files must end with newline
  3. Run rake autofix - Let Prettier/RuboCop handle formatting
  4. Update CHANGELOG - Document the error handling change
  5. Update documentation - Explain the error format change if intentional

💡 Suggestions

  1. Split this PR: Consider separating the error handling change from the test additions

  2. Write meaningful concurrency tests:

    test('handles concurrent RSC renders without race conditions', async () => {
      const promises = Array.from({ length: 10 }, (_, i) =>
        ReactOnRails.serverRenderRSCReactComponent({
          railsContext: mockRailsContext,
          name: `Component${i}`,
          // ... other options
        })
      );
      const results = await Promise.all(promises);
      // Assert all renders succeeded without conflicts
    });
  3. Add test for the error handling change:

    test('returns JSON error format when rendering fails', async () => {
      // Test the new JSON error response behavior
    });
  4. Improve test quality:

    • Add descriptive test names
    • Assert on actual behavior
    • Test edge cases
    • Ensure proper cleanup

🎯 Summary

Please address:

  1. Clarify the purpose of the error handling change
  2. Fix the test implementation (name, assertions, semicolons)
  3. Consider removing mock-fs and using Jest's native mocking
  4. Add actual concurrency tests if that's the intent
  5. Complete the PR checklist items
  6. Run linting: bundle exec rubocop and rake autofix

The concept of adding concurrency tests is valuable, but the current implementation needs refinement before merging. Happy to discuss any of these points!

@AbanoubGhadban AbanoubGhadban force-pushed the abanoubghadban/add-concurrency-tests branch from ef3ce5f to 713033b Compare October 30, 2025 06:59
@claude
Copy link

claude bot commented Oct 30, 2025

Code Review for PR #1890: Add Concurrency Tests

Summary

This PR adds a test for React Server Components (RSC) rendering, removes reactClientManifestFileName from the RSCRenderParams interface, and adds debug console.log statements to the streaming utilities. However, the PR has several critical issues that need to be addressed before merging.


Critical Issues

1. Debug Console Statements Left in Production Code 🚨

Location: packages/react-on-rails-pro/src/streamingUtils.ts:114, 130, 148

console.log("Stream working");
console.log("Stream flushed");
console.log("Stream ended");

Issue: Debug console.log statements should never be committed to production code. These will pollute logs and impact performance.

Action Required: Remove all debug console.log statements before merging.


2. Test Quality Issues 🚨

Location: packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx

Multiple problems with the new test:

a) Poor test naming:

test('eeee', async () => {

This is completely unacceptable. Test names should clearly describe what is being tested.

b) Incomplete test implementation:

  • No assertions about the actual rendering behavior
  • Only checks that mock-fs works
  • Doesn't verify RSC-specific functionality
  • Missing error handling tests
  • No validation of stream output format

c) Hardcoded file paths:

console.log(path.resolve('./src/react-client-manifest.json'));

More debug console.log statements that shouldn't be committed.

d) Mock filesystem setup issues:

  • The mock only creates react-client-manifest.json but the code also needs react-server-client-manifest.json (see packages/react-on-rails-pro/src/getReactServerComponent.server.ts:90-91)
  • This will likely cause the test to fail when actually attempting to render

e) Missing cleanup and error scenarios:

  • No tests for error conditions
  • No tests for concurrent rendering scenarios (despite PR title mentioning "concurrency")
  • Doesn't follow the established testing patterns seen in streamServerRenderedReactComponent.test.jsx

Recommendation: Look at packages/react-on-rails-pro/tests/streamServerRenderedReactComponent.test.jsx as a reference. It demonstrates:

  • Clear, descriptive test names
  • Proper setup and teardown
  • Error handling tests
  • Multiple test cases covering different scenarios
  • No debug console statements

3. Type Change Impact Not Fully Considered

Location: packages/react-on-rails/src/types/index.ts:226-228

The removal of reactClientManifestFileName from RSCRenderParams:

export interface RSCRenderParams extends Omit<RenderParams, 'railsContext'> {
  railsContext: RailsContextWithServerStreamingCapabilities;
- reactClientManifestFileName: string;
}

Analysis:

  • This appears to be a valid refactoring since reactClientManifestFileName is already available in railsContext (see packages/react-on-rails-pro/src/ReactOnRailsRSC.ts:47)
  • However, the PR description doesn't explain this change or its rationale
  • Need to verify no external consumers depend on this property

Action Required: Add a note in the PR description explaining this type change and why it's safe.


Performance Considerations

  1. Mock-fs Performance: The test uses mock-fs which can be slow and flaky. Consider using actual test fixtures in a temporary directory instead.

  2. No Concurrency Testing: Despite the PR title "Add Concurrency Tests", there are no tests that actually validate concurrent rendering behavior. The test only checks that a single component can be rendered.


Security Concerns

No security issues identified in the changed code.


Test Coverage Issues

The new test provides minimal coverage:

  • ❌ No error handling tests
  • ❌ No concurrent rendering tests (despite PR title)
  • ❌ No validation of output format
  • ❌ No edge case testing
  • ✅ Basic filesystem mocking setup works

Coverage Gap: For comparison, streamServerRenderedReactComponent.test.jsx has:

  • 10+ test cases covering various scenarios
  • Error handling for both sync and async errors
  • Tests for different component types
  • Proper assertions on stream output

Code Quality Issues

  1. Missing PR checklist completion:

    • Add/update test to cover these changes
    • Update documentation
    • Update CHANGELOG file
  2. Inadequate PR description: The PR description is still using the template placeholder text and doesn't explain:

    • What concurrency issues this addresses
    • Why the type change was made
    • What the test validates
  3. CLAUDE.md Compliance:
    According to the project's CLAUDE.md:

    • ⚠️ All changes must pass bundle exec rubocop (need to verify CI status)
    • ⚠️ Files must end with newline characters
    • ⚠️ Never commit without running full lint check

Recommendations

Before this PR can be merged:

  1. Remove ALL debug console.log statements from streamingUtils.ts
  2. Rewrite the test with:
    • A descriptive name like: test('renders RSC component and streams NDJSON output', async () => {
    • Proper assertions validating the rendered output
    • Error handling tests
    • Multiple test cases (success, error, concurrent scenarios)
    • Follow the pattern from streamServerRenderedReactComponent.test.jsx
  3. Add both manifest files to mock-fs or use a better testing approach
  4. Update PR description to explain the changes and rationale
  5. Complete the PR checklist items
  6. Run linting: bundle exec rubocop and fix any violations
  7. Consider renaming the PR if it's not actually testing concurrency, or add actual concurrency tests

If the goal is to add concurrency tests, I'd expect to see:

  • Tests that render multiple components simultaneously
  • Tests that verify stream isolation between concurrent requests
  • Tests that check for race conditions or state leaks

Positive Notes

✅ The type refactoring to remove reactClientManifestFileName from RSCRenderParams appears valid
✅ Adding test infrastructure for RSC rendering is a good initiative
✅ Using mock-fs allows testing without filesystem dependencies


Verdict

Changes Requested - This PR needs significant work before it can be merged. The debug statements are blockers, and the test quality needs substantial improvement to provide value.

Please address the critical issues above and feel free to reach out if you need help with the test implementation.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ef3ce5f and 713033b.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (4)
  • packages/react-on-rails-pro/package.json (1 hunks)
  • packages/react-on-rails-pro/src/streamingUtils.ts (3 hunks)
  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx (1 hunks)
  • packages/react-on-rails/src/types/index.ts (0 hunks)
💤 Files with no reviewable changes (1)
  • packages/react-on-rails/src/types/index.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/react-on-rails-pro/src/streamingUtils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/react-on-rails-pro/package.json
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,jsx,ts,tsx,css,scss,json,yml,yaml,md}

📄 CodeRabbit inference engine (CLAUDE.md)

Prettier is the sole authority for formatting all non-Ruby files; never manually format them

Files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint for JS/TS code (lint via rake lint or yarn lint)

Files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
🧠 Learnings (8)
📓 Common learnings
Learnt from: alexeyr-ci
PR: shakacode/react_on_rails#1644
File: node_package/src/ReactOnRailsRSC.ts:87-87
Timestamp: 2024-12-12T13:07:09.929Z
Learning: When handling errors in 'node_package/src/ReactOnRailsRSC.ts', include the error stack in error messages in development and test environments to aid debugging.
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1696
File: node_package/src/RSCPayloadGenerator.ts:124-168
Timestamp: 2025-06-09T07:58:02.646Z
Learning: In React Server Components (RSC) implementations, explicit error handling in RSC payload generation streams (like in RSCPayloadGenerator.ts) is not needed because errors will propagate to client components that fetch the RSC payloads (such as RSCRoute components). React's built-in error handling mechanisms and ErrorBoundary components are sufficient for handling these errors at the component level, which allows for better user experience with appropriate error UI fallbacks.
📚 Learning: 2024-12-12T13:07:09.929Z
Learnt from: alexeyr-ci
PR: shakacode/react_on_rails#1644
File: node_package/src/ReactOnRailsRSC.ts:87-87
Timestamp: 2024-12-12T13:07:09.929Z
Learning: When handling errors in 'node_package/src/ReactOnRailsRSC.ts', include the error stack in error messages in development and test environments to aid debugging.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
📚 Learning: 2025-02-13T19:09:15.991Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: node_package/src/RSCWebpackLoader.ts:0-0
Timestamp: 2025-02-13T19:09:15.991Z
Learning: In React Server Components webpack loader, using `new Function('return import("react-server-dom-webpack/node-loader")')()` is necessary as a workaround to bypass TypeScript compilation issues with direct dynamic imports.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
PR: shakacode/react_on_rails#1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
📚 Learning: 2025-06-09T07:58:02.646Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1696
File: node_package/src/RSCPayloadGenerator.ts:124-168
Timestamp: 2025-06-09T07:58:02.646Z
Learning: In React Server Components (RSC) implementations, explicit error handling in RSC payload generation streams (like in RSCPayloadGenerator.ts) is not needed because errors will propagate to client components that fetch the RSC payloads (such as RSCRoute components). React's built-in error handling mechanisms and ErrorBoundary components are sufficient for handling these errors at the component level, which allows for better user experience with appropriate error UI fallbacks.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
📚 Learning: 2024-07-27T10:08:35.868Z
Learnt from: theforestvn88
PR: shakacode/react_on_rails#1620
File: spec/dummy/client/app/startup/HelloTurboStream.jsx:3-3
Timestamp: 2024-07-27T10:08:35.868Z
Learning: The `RailsContext` import in `spec/dummy/client/app/startup/HelloTurboStream.jsx` is used later in the project, as clarified by the user theforestvn88.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
📚 Learning: 2025-07-08T05:57:29.630Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1745
File: node_package/src/RSCRequestTracker.ts:8-14
Timestamp: 2025-07-08T05:57:29.630Z
Learning: The global `generateRSCPayload` function in React on Rails Pro (RORP) is provided by the framework during rendering requests, not implemented in application code. The `declare global` statements are used to document the expected interface that RORP will inject at runtime.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
🧬 Code graph analysis (1)
packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx (1)
packages/react-on-rails/src/types/index.ts (2)
  • ReactOnRails (276-344)
  • RailsContextWithServerStreamingCapabilities (65-68)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: lint-js-and-ruby
  • GitHub Check: examples (3.4, latest)
  • GitHub Check: claude-review
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: build-dummy-app-webpack-test-bundles (3.4, 22)
🔇 Additional comments (2)
packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx (2)

19-21: Add missing semicolon.

Apply this diff:

 afterAll(() => {
   mock.restore();
-})
+});

Likely an incorrect or invalid review comment.


11-11: Missing React import for JSX syntax.

The component uses JSX but React is not imported, which will cause a compilation or runtime error.

Apply this diff to add the React import:

+import React from 'react';
+
 const Component1 = () => <div>HelloWorld</div>;
⛔ Skipped due to learnings
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: node_package/src/RSCWebpackLoader.ts:0-0
Timestamp: 2025-02-13T19:09:15.991Z
Learning: In React Server Components webpack loader, using `new Function('return import("react-server-dom-webpack/node-loader")')()` is necessary as a workaround to bypass TypeScript compilation issues with direct dynamic imports.
Learnt from: theforestvn88
PR: shakacode/react_on_rails#1620
File: spec/dummy/client/app/startup/HelloTurboStream.jsx:3-3
Timestamp: 2024-07-27T10:08:35.868Z
Learning: The `RailsContext` import in `spec/dummy/client/app/startup/HelloTurboStream.jsx` is used later in the project, as clarified by the user theforestvn88.
Learnt from: theforestvn88
PR: shakacode/react_on_rails#1620
File: spec/dummy/client/app/startup/HelloTurboStream.jsx:3-3
Timestamp: 2024-10-08T20:53:47.076Z
Learning: The `RailsContext` import in `spec/dummy/client/app/startup/HelloTurboStream.jsx` is used later in the project, as clarified by the user theforestvn88.
Learnt from: alexeyr-ci
PR: shakacode/react_on_rails#1644
File: node_package/src/ReactOnRailsRSC.ts:87-87
Timestamp: 2024-12-12T13:07:09.929Z
Learning: When handling errors in 'node_package/src/ReactOnRailsRSC.ts', include the error stack in error messages in development and test environments to aid debugging.
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Learnt from: justin808
PR: shakacode/react_on_rails#1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1696
File: node_package/src/RSCPayloadGenerator.ts:124-168
Timestamp: 2025-06-09T07:58:02.646Z
Learning: In React Server Components (RSC) implementations, explicit error handling in RSC payload generation streams (like in RSCPayloadGenerator.ts) is not needed because errors will propagate to client components that fetch the RSC payloads (such as RSCRoute components). React's built-in error handling mechanisms and ErrorBoundary components are sufficient for handling these errors at the component level, which allows for better user experience with appropriate error UI fallbacks.
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation is handled in deeper level calls of the React on Rails Pro codebase, so it doesn't need to be validated again in the `rsc_payload_react_component` helper method.
Learnt from: Romex91
PR: shakacode/react_on_rails#1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Learnt from: alexeyr-ci2
PR: shakacode/react_on_rails#1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.

Comment on lines 23 to 25
test('eeee', async () => {
console.log(path.resolve('./src/react-client-manifest.json'));
expect(fs.readFileSync('./src/react-client-manifest.json').toString()).toEqual('{}');
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Improve test name and remove debug code.

The test name "eeee" is not descriptive and should clearly state what functionality is being tested. The console.log on line 24 appears to be debug code that should be removed.

Apply this diff:

-test('eeee', async () => {
-  console.log(path.resolve('./src/react-client-manifest.json'));
+test('should render RSC component with server streaming', async () => {
   expect(fs.readFileSync('./src/react-client-manifest.json').toString()).toEqual('{}');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test('eeee', async () => {
console.log(path.resolve('./src/react-client-manifest.json'));
expect(fs.readFileSync('./src/react-client-manifest.json').toString()).toEqual('{}');
test('should render RSC component with server streaming', async () => {
expect(fs.readFileSync('./src/react-client-manifest.json').toString()).toEqual('{}');
🤖 Prompt for AI Agents
In packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
around lines 23 to 25, the test uses a non-descriptive name "eeee" and contains
a console.log debug statement; rename the test to a meaningful description
(e.g., "reads react-client-manifest and expects empty JSON") and remove the
console.log line so the test only reads and asserts the file content.

Comment on lines 26 to 35
const readable = ReactOnRails.serverRenderRSCReactComponent({
railsContext: {
reactClientManifestFileName: 'react-client-manifest.json',
reactServerClientManifestFileName: 'react-server-client-manifest.json',
} as unknown as RailsContextWithServerStreamingCapabilities,
name: 'Component1',
renderingReturnsPromises: true,
throwJsErrors: true,
domNodeId: 'dom-id',
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix incomplete railsContext and remove unsafe type assertion.

The railsContext object is missing required methods getRSCPayloadStream and addPostSSRHook from the RailsContextWithServerStreamingCapabilities interface. Additionally, the type assertion as unknown as bypasses type checking, hiding this issue. The property reactClientManifestFileName was removed from the interface according to the PR changes.

You need to provide a complete mock implementation of the railsContext. Here's a suggested approach:

-  const readable = ReactOnRails.serverRenderRSCReactComponent({
-    railsContext: {
-      reactClientManifestFileName: 'react-client-manifest.json',
-      reactServerClientManifestFileName: 'react-server-client-manifest.json',
-    } as unknown as RailsContextWithServerStreamingCapabilities,
+  const mockRailsContext: RailsContextWithServerStreamingCapabilities = {
+    reactServerClientManifestFileName: 'react-server-client-manifest.json',
+    getRSCPayloadStream: async (componentName: string, props: unknown) => {
+      // Mock implementation
+      const { Readable } = require('stream');
+      return Readable.from([Buffer.from('mock stream data')]);
+    },
+    addPostSSRHook: (hook: () => void) => {
+      // Mock implementation
+    },
+    // Add any other required properties from RailsContextWithServerComponentMetadata
+  };
+
+  const readable = ReactOnRails.serverRenderRSCReactComponent({
+    railsContext: mockRailsContext,
     name: 'Component1',
🤖 Prompt for AI Agents
In packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
around lines 26-35, the railsContext mock is incomplete and uses an unsafe "as
unknown as" assertion; replace it with a properly typed mock that implements the
RailsContextWithServerStreamingCapabilities interface (remove the now-removed
reactClientManifestFileName property and only include the valid manifest name),
provide a getRSCPayloadStream method that returns a readable stream (or a simple
mock Readable like an object with a pipe/reader compatible for the test), and
implement addPostSSRHook as a no-op or collector function; then declare the mock
with the correct type (no unknown cast) and pass that to
serverRenderRSCReactComponent so TypeScript verifies the required methods exist.

Comment on lines 37 to 45
const decoder = new TextDecoder();
readable.on('data', chunk => {
console.log(decoder.decode(chunk));
})
await new Promise((resolve, reject) => {
readable.on('end', resolve);
readable.on('error', reject);
})
}, 100000)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add assertions and remove debug logging.

The test lacks meaningful assertions on the stream output from serverRenderRSCReactComponent. Currently, it only verifies the mock file exists but doesn't validate the actual rendering result. The console.log on line 39 appears to be debug code. Additionally, the 100-second timeout is excessive and should be reduced or explained.

Apply this diff:

   const decoder = new TextDecoder();
+  let output = '';
   readable.on('data', chunk => {
-    console.log(decoder.decode(chunk));
+    output += decoder.decode(chunk);
   })
   await new Promise((resolve, reject) => {
     readable.on('end', resolve);
     readable.on('error', reject);
-  })
-}, 100000)
+  });
+
+  // Add assertions to verify the rendered output
+  expect(output).toBeTruthy();
+  expect(output).toContain('HelloWorld'); // or whatever the expected output format is
+}, 10000); // Reduce timeout to 10 seconds
🤖 Prompt for AI Agents
In packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
around lines 37 to 45, remove the debug console.log and replace the ad-hoc data
handler with collection of decoded chunks into a buffer string, then assert that
the final buffered output contains the expected rendered content (e.g., expected
component HTML/marker or specific text produced by
serverRenderRSCReactComponent) after the stream ends; keep error handling for
'error' events and reduce the test timeout from 100000ms to a reasonable value
(e.g., 5000ms) or remove the explicit timeout if unnecessary.

@AbanoubGhadban AbanoubGhadban force-pushed the abanoubghadban/add-concurrency-tests branch from 713033b to add0cd8 Compare October 30, 2025 15:12
@claude
Copy link

claude bot commented Oct 30, 2025

Code Review for PR #1890: Add Concurrency Tests

Thank you for contributing these concurrency tests for React Server Components! This is valuable test coverage for ensuring RSC streaming works correctly under concurrent rendering scenarios.

Code Quality and Best Practices

Strengths:

  • Well-structured test utilities (AsyncQueue, StreamReader) that provide reusable abstractions for testing streaming behavior
  • Good separation of concerns with dedicated test helper classes
  • Comprehensive test scenarios covering both single and parallel rendering

Issues to Address:

  1. Missing PR Description (packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx:1)

    • The PR template checklist is not filled out. Please:
      • Add a proper summary describing what these tests cover
      • Check off the completed items in the checklist
      • Update the CHANGELOG file as indicated
      • Add documentation if needed
  2. Code Style Issues (packages/react-on-rails-pro/tests/AsyncQueue.ts:34)

    • Missing space after if keyword (line 34): let teardown = () => {}
    • This violates standard JavaScript formatting conventions
    • Run rake autofix to fix formatting issues automatically per CLAUDE.md
  3. Unused Imports (packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx:10)

    • Several unused imports: pipeline, finished, buildServerRenderer, createReactOutput, fs
    • Clean these up to reduce noise and improve code clarity
  4. Commented Code (packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx:91-96)

    • Lines 91-92 and 96 contain commented assertions
    • Either remove them or uncomment with proper explanation if they're needed
  5. Inconsistent Spacing (packages/react-on-rails-pro/tests/AsyncQueue.ts:33-34)

    • Inconsistent spacing around variable declarations
    • Let Prettier handle all formatting by running rake autofix

Potential Bugs and Issues

  1. EventEmitter Type Issues (packages/react-on-rails-pro/tests/AsyncQueue.ts:4)

    • EventEmitter<{ data: any, end: any }> uses generic syntax that may not be supported by Node's EventEmitter
    • Standard Node.js EventEmitter doesn't accept generic type parameters
    • This might cause runtime issues or TypeScript compilation errors
    • Consider using EventEmitter without generics and typing the event handlers instead
  2. Race Condition in AsyncQueue (packages/react-on-rails-pro/tests/AsyncQueue.ts:26-29)

    • In dequeue(), checking bufferValueIfExist then isEnded could miss items if queue ends while buffer has items
    • While unlikely in test scenarios, it's technically a race condition
  3. Memory Leak Risk (packages/react-on-rails-pro/tests/AsyncQueue.ts:44-49)

    • If the Promise returned by dequeue() is never awaited/caught, event listeners remain attached
    • Consider adding timeout or explicit cleanup mechanism
  4. Error Handling (packages/react-on-rails-pro/tests/StreamReader.ts:19-24)

    • The stream end handler checks readableStream.closed but also adds an 'end' listener
    • If the stream is already closed, the 'end' event won't fire, but you've handled this correctly
    • However, no error handling for stream errors - consider adding .on('error', ...) handler
  5. Type Safety (packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx:93)

    • Array operations like regexMatch[1] could be undefined
    • Use optional chaining or proper guards: const chunkLine.slice(...)

Performance Considerations

  1. Interval Timing (packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx:32-35)

    • Using setInterval with 1ms delay and 50 iterations is inefficient
    • Consider using setImmediate() or process.nextTick() for faster, more efficient async testing
    • Alternatively, use a single setTimeout() instead of counting intervals
  2. Large Parallel Renders (packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx:152)

    • Creating 50 parallel renders is good for stress testing
    • However, this might be slow in CI environments
    • Consider parametrizing this or adding a comment explaining the choice
  3. Mock-fs Overhead (packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx:52-57)

    • mock-fs is initialized for all tests but only restored in afterAll
    • This is fine, but ensure it doesn't interfere with other test files
    • Consider using beforeAll to make the setup/teardown symmetric

Security Concerns

  1. JSON Parsing Without Validation (packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx:85)

    • JSON.parse(chunk) and subsequent parsing without try-catch could throw
    • While this is test code, proper error handling would make failures clearer
    • Wrap in try-catch with meaningful error messages
  2. Mock-fs Security (packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx:52)

    • mock-fs can cause issues if not properly restored
    • Good that you have afterAll(() => mock.restore())
    • Consider adding a safety check in case tests fail before reaching afterAll

Test Coverage and Quality

Strengths:

  • Excellent concurrency testing that validates no data leakage between parallel renders
  • Good use of async/await patterns for testing streaming behavior
  • Tests verify both the structure and content of streamed output

Improvements Needed:

  1. Test Assertions (packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx:123)

    • expect.assertions(258) is very specific but brittle
    • If you add/remove one expectation, this breaks
    • Consider removing this or documenting why exactly 258 assertions are needed
  2. Console Logs in Tests (packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx:17-18)

    • Test components include console.log statements
    • These will clutter test output - remove them or use a test logger
    • Lines 28-29 also have console.logs that should be removed
  3. Incomplete Assertions (packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx:91-96)

    • Commented assertions suggest incomplete test validation
    • Either complete these tests or document why they're commented
  4. Test Descriptions (packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx:122)

    • Test name is good but could be more specific: "Renders concurrent RSC streams without data leakage"
    • Add comments explaining what each section of the test validates

Breaking Change Analysis

Critical Issue:
The removal of reactClientManifestFileName from RSCRenderParams interface (packages/react-on-rails/src/types/index.ts:227) appears to be a breaking change but:

  • No usage of this field is shown being updated in the diff
  • This could break existing code that passes this parameter
  • The test file shows it being passed in railsContext instead, which makes sense
  • Please document this breaking change in the CHANGELOG and ensure all usages are updated

Required Actions Before Merge

Critical (Must Fix):

  1. Run bundle exec rubocop and fix all violations (per CLAUDE.md requirements)
  2. Run rake autofix to fix formatting issues
  3. Add proper PR description and check off the template items
  4. Remove or document the reactClientManifestFileName breaking change
  5. Remove console.log statements from test components
  6. Clean up unused imports

High Priority (Should Fix):

  1. Fix EventEmitter typing issue in AsyncQueue.ts
  2. Add error handling to StreamReader
  3. Remove or complete commented test assertions
  4. Document why 258 assertions are needed or remove the count

Nice to Have:

  1. Consider using setImmediate instead of setInterval for performance
  2. Add JSDoc comments to test utility classes
  3. Add more descriptive test names and inline comments

Verdict

This PR adds valuable test coverage for concurrency scenarios in RSC rendering. The test utilities are well-designed and reusable. However, there are several issues that need to be addressed before merging, particularly around code quality, the breaking change, and adherence to the project's mandatory linting requirements per CLAUDE.md.

Please ensure you run bundle exec rubocop before pushing, as this is a non-negotiable requirement per CLAUDE.md.

Looking forward to seeing these improvements!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx (1)

65-86: Mock the full streaming railsContext instead of casting.
RailsContextWithServerStreamingCapabilities requires getRSCPayloadStream and addPostSSRHook. Casting a plain object with only manifest names (as unknown as) means ReactOnRails.serverRenderRSCReactComponent will throw at runtime the first time it tries to call either hook, so this test currently cannot run once the production code exercises those methods. Please build a single shared mock that actually implements the interface—e.g. create a helper that wires buildServerRenderer + createReactOutput to return a Readable stream, define addPostSSRHook as a no-op collector, drop the obsolete reactClientManifestFileName, and pass that typed object into both renders without any unsafe casts.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 713033b and add0cd8.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (6)
  • packages/react-on-rails-pro/package.json (1 hunks)
  • packages/react-on-rails-pro/tests/AsyncQueue.ts (1 hunks)
  • packages/react-on-rails-pro/tests/StreamReader.ts (1 hunks)
  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx (1 hunks)
  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx (1 hunks)
  • packages/react-on-rails/src/types/index.ts (0 hunks)
💤 Files with no reviewable changes (1)
  • packages/react-on-rails/src/types/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/react-on-rails-pro/package.json
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,jsx,ts,tsx,css,scss,json,yml,yaml,md}

📄 CodeRabbit inference engine (CLAUDE.md)

Prettier is the sole authority for formatting all non-Ruby files; never manually format them

Files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
  • packages/react-on-rails-pro/tests/AsyncQueue.ts
  • packages/react-on-rails-pro/tests/StreamReader.ts
  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint for JS/TS code (lint via rake lint or yarn lint)

Files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
  • packages/react-on-rails-pro/tests/AsyncQueue.ts
  • packages/react-on-rails-pro/tests/StreamReader.ts
  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
🧠 Learnings (12)
📓 Common learnings
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1696
File: node_package/src/RSCPayloadGenerator.ts:124-168
Timestamp: 2025-06-09T07:58:02.646Z
Learning: In React Server Components (RSC) implementations, explicit error handling in RSC payload generation streams (like in RSCPayloadGenerator.ts) is not needed because errors will propagate to client components that fetch the RSC payloads (such as RSCRoute components). React's built-in error handling mechanisms and ErrorBoundary components are sufficient for handling these errors at the component level, which allows for better user experience with appropriate error UI fallbacks.
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1696
File: node_package/src/transformRSCNodeStream.ts:3-31
Timestamp: 2025-04-09T13:02:16.009Z
Learning: In the React Server Components (RSC) stream processing implementation, all chunks end with a newline character (`\n`), so the `lastIncompleteChunk` variable is expected to be empty by the time the stream ends, making a flush handler unnecessary in the Transform stream.
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: node_package/src/RSCWebpackLoader.ts:0-0
Timestamp: 2025-02-13T19:09:15.991Z
Learning: In React Server Components webpack loader, using `new Function('return import("react-server-dom-webpack/node-loader")')()` is necessary as a workaround to bypass TypeScript compilation issues with direct dynamic imports.
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1696
File: node_package/src/RSCProvider.tsx:0-0
Timestamp: 2025-06-11T12:34:58.182Z
Learning: `RSCProvider` intentionally keeps failed `getServerComponent` promises cached (even if rejected) to avoid repeated fetch attempts; callers must use `refetchComponent` to retry.
📚 Learning: 2024-12-12T13:07:09.929Z
Learnt from: alexeyr-ci
PR: shakacode/react_on_rails#1644
File: node_package/src/ReactOnRailsRSC.ts:87-87
Timestamp: 2024-12-12T13:07:09.929Z
Learning: When handling errors in 'node_package/src/ReactOnRailsRSC.ts', include the error stack in error messages in development and test environments to aid debugging.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
📚 Learning: 2025-02-13T19:09:15.991Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: node_package/src/RSCWebpackLoader.ts:0-0
Timestamp: 2025-02-13T19:09:15.991Z
Learning: In React Server Components webpack loader, using `new Function('return import("react-server-dom-webpack/node-loader")')()` is necessary as a workaround to bypass TypeScript compilation issues with direct dynamic imports.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
📚 Learning: 2025-06-09T07:58:02.646Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1696
File: node_package/src/RSCPayloadGenerator.ts:124-168
Timestamp: 2025-06-09T07:58:02.646Z
Learning: In React Server Components (RSC) implementations, explicit error handling in RSC payload generation streams (like in RSCPayloadGenerator.ts) is not needed because errors will propagate to client components that fetch the RSC payloads (such as RSCRoute components). React's built-in error handling mechanisms and ErrorBoundary components are sufficient for handling these errors at the component level, which allows for better user experience with appropriate error UI fallbacks.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
📚 Learning: 2024-07-27T10:08:35.868Z
Learnt from: theforestvn88
PR: shakacode/react_on_rails#1620
File: spec/dummy/client/app/startup/HelloTurboStream.jsx:3-3
Timestamp: 2024-07-27T10:08:35.868Z
Learning: The `RailsContext` import in `spec/dummy/client/app/startup/HelloTurboStream.jsx` is used later in the project, as clarified by the user theforestvn88.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
PR: shakacode/react_on_rails#1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
📚 Learning: 2025-07-08T05:57:29.630Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1745
File: node_package/src/RSCRequestTracker.ts:8-14
Timestamp: 2025-07-08T05:57:29.630Z
Learning: The global `generateRSCPayload` function in React on Rails Pro (RORP) is provided by the framework during rendering requests, not implemented in application code. The `declare global` statements are used to document the expected interface that RORP will inject at runtime.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation is handled in deeper level calls of the React on Rails Pro codebase, so it doesn't need to be validated again in the `rsc_payload_react_component` helper method.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
📚 Learning: 2025-10-23T17:22:01.074Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.

Applied to files:

  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
📚 Learning: 2025-04-09T13:02:16.009Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1696
File: node_package/src/transformRSCNodeStream.ts:3-31
Timestamp: 2025-04-09T13:02:16.009Z
Learning: In the React Server Components (RSC) stream processing implementation, all chunks end with a newline character (`\n`), so the `lastIncompleteChunk` variable is expected to be empty by the time the stream ends, making a flush handler unnecessary in the Transform stream.

Applied to files:

  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
🧬 Code graph analysis (3)
packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx (1)
packages/react-on-rails/src/types/index.ts (1)
  • RailsContextWithServerStreamingCapabilities (65-68)
packages/react-on-rails-pro/tests/StreamReader.ts (1)
react_on_rails_pro/packages/node-renderer/tests/htmlStreaming.test.js (1)
  • decoder (88-88)
packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx (4)
packages/react-on-rails/src/types/index.ts (1)
  • RailsContextWithServerStreamingCapabilities (65-68)
packages/react-on-rails-pro/tests/testUtils.js (1)
  • stream (14-21)
packages/react-on-rails-pro/tests/AsyncQueue.ts (1)
  • enqueue (8-18)
packages/react-on-rails-pro/tests/StreamReader.ts (1)
  • nextChunk (28-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: lint-js-and-ruby
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: claude-review
  • GitHub Check: build
  • GitHub Check: rspec-package-tests (3.4, minimum)
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: rspec-package-tests (3.4, latest)
  • GitHub Check: rspec-package-tests (3.2, latest)
  • GitHub Check: rspec-package-tests (3.2, minimum)

Comment on lines +27 to +52
const bufferValueIfExist = this.buffer.shift();
if (bufferValueIfExist) {
resolve(bufferValueIfExist);
} else if (this.isEnded) {
reject(new Error("Queue Ended"));
} else {
let teardown = () => {}
const onData = (value: T) => {
resolve(value);
teardown();
}

const onEnd = () => {
reject(new Error("Queue Ended"));
teardown();
}

this.eventEmitter.on('data', onData);
this.eventEmitter.on('end', onEnd);
teardown = () => {
this.eventEmitter.off('data', onData);
this.eventEmitter.off('end', onEnd);
}
}
})
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Dequeue must honor falsy buffered values.
buffer.shift() can legitimately return '', 0, or false, but the current truthiness check falls through to the “queue ended” branch and rejects even though data is buffered. Compare against undefined instead so every enqueued value is delivered.

-      const bufferValueIfExist = this.buffer.shift();
-      if (bufferValueIfExist) {
-        resolve(bufferValueIfExist);
+      const bufferValue = this.buffer.shift();
+      if (bufferValue !== undefined) {
+        resolve(bufferValue);
       } else if (this.isEnded) {
         reject(new Error("Queue Ended"));
       } else {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const bufferValueIfExist = this.buffer.shift();
if (bufferValueIfExist) {
resolve(bufferValueIfExist);
} else if (this.isEnded) {
reject(new Error("Queue Ended"));
} else {
let teardown = () => {}
const onData = (value: T) => {
resolve(value);
teardown();
}
const onEnd = () => {
reject(new Error("Queue Ended"));
teardown();
}
this.eventEmitter.on('data', onData);
this.eventEmitter.on('end', onEnd);
teardown = () => {
this.eventEmitter.off('data', onData);
this.eventEmitter.off('end', onEnd);
}
}
})
}
const bufferValue = this.buffer.shift();
if (bufferValue !== undefined) {
resolve(bufferValue);
} else if (this.isEnded) {
reject(new Error("Queue Ended"));
} else {
let teardown = () => {}
const onData = (value: T) => {
resolve(value);
teardown();
}
const onEnd = () => {
reject(new Error("Queue Ended"));
teardown();
}
this.eventEmitter.on('data', onData);
this.eventEmitter.on('end', onEnd);
teardown = () => {
this.eventEmitter.off('data', onData);
this.eventEmitter.off('end', onEnd);
}
}
})
}
🤖 Prompt for AI Agents
In packages/react-on-rails-pro/tests/AsyncQueue.ts around lines 27 to 52, the
current truthiness check on the shifted buffer value causes legitimate falsy
values (0, '', false) to be treated as missing and triggers the "Queue Ended"
rejection; change the check to compare explicitly against undefined (e.g. const
bufferValue = this.buffer.shift(); if (bufferValue !== undefined) {
resolve(bufferValue); }) so that all queued values, including falsy ones, are
delivered while preserving the existing end/emit logic.

Comment on lines +63 to +74
return ReactOnRails.serverRenderRSCReactComponent({
railsContext: {
reactClientManifestFileName: 'react-client-manifest.json',
reactServerClientManifestFileName: 'react-server-client-manifest.json',
} as unknown as RailsContextWithServerStreamingCapabilities,
name: 'AsyncQueueContainer',
renderingReturnsPromises: true,
throwJsErrors: true,
domNodeId: 'dom-id',
props,
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Provide a real RailsContextWithServerStreamingCapabilities implementation.
This helper reuses the same incomplete railsContext mock: no getRSCPayloadStream, no addPostSSRHook, and it still references the removed reactClientManifestFileName. The unsafe as unknown as masks the type error, but the first production call to either missing method will explode. Extract a properly typed mock (shared with the other test) that actually implements the streaming hooks—using buildServerRenderer/createReactOutput to supply a Readable payload and a no-op addPostSSRHook—and drop the deprecated manifest field. Then pass that typed object directly so the compiler enforces the contract.

🤖 Prompt for AI Agents
In packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
around lines 63–74, the test is using an unsafe incomplete railsContext mock
(casting with as unknown as) that lacks getRSCPayloadStream and addPostSSRHook
and still contains the removed reactClientManifestFileName; replace this with a
real RailsContextWithServerStreamingCapabilities object: extract a properly
typed mock shared with the other test, remove the deprecated
reactClientManifestFileName field, implement getRSCPayloadStream to return a
Readable payload (use the existing buildServerRenderer/createReactOutput helpers
to produce the stream) and implement addPostSSRHook as a no-op, then pass that
typed object directly (no unsafe cast) so the compiler enforces the contract.

Comment on lines +14 to +25
readableStream.on('data', (chunk) => {
const decodedChunk = decoder.decode(chunk);
this.asyncQueue.enqueue(decodedChunk);
});

if (readableStream.closed) {
this.asyncQueue.end();
} else {
readableStream.on('end', () => {
this.asyncQueue.end();
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use streaming TextDecoder decoding and flush at end.
Decoding each chunk with decoder.decode(chunk) resets the decoder state, so any multi-byte character split across chunks will be corrupted or replaced. We also drop the final buffered bytes because we never flush at end. Decode with { stream: true }, flush once on end, and only then close the queue; this keeps UTF‑8 intact and preserves the tail chunk.

-    readableStream.on('data', (chunk) => {
-      const decodedChunk = decoder.decode(chunk);
-      this.asyncQueue.enqueue(decodedChunk);
-    });
-
-    if (readableStream.closed) {
-      this.asyncQueue.end();
-    } else {
-      readableStream.on('end', () => {
-        this.asyncQueue.end();
-      });
-    }
+    readableStream.on('data', (chunk) => {
+      const decodedChunk = decoder.decode(chunk, { stream: true });
+      if (decodedChunk.length > 0) {
+        this.asyncQueue.enqueue(decodedChunk);
+      }
+    });
+
+    readableStream.on('end', () => {
+      const flushed = decoder.decode();
+      if (flushed.length > 0) {
+        this.asyncQueue.enqueue(flushed);
+      }
+      this.asyncQueue.end();
+    });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
readableStream.on('data', (chunk) => {
const decodedChunk = decoder.decode(chunk);
this.asyncQueue.enqueue(decodedChunk);
});
if (readableStream.closed) {
this.asyncQueue.end();
} else {
readableStream.on('end', () => {
this.asyncQueue.end();
});
}
readableStream.on('data', (chunk) => {
const decodedChunk = decoder.decode(chunk, { stream: true });
if (decodedChunk.length > 0) {
this.asyncQueue.enqueue(decodedChunk);
}
});
readableStream.on('end', () => {
const flushed = decoder.decode();
if (flushed.length > 0) {
this.asyncQueue.enqueue(flushed);
}
this.asyncQueue.end();
});
🤖 Prompt for AI Agents
In packages/react-on-rails-pro/tests/StreamReader.ts around lines 14–25, the
code decodes each chunk with decoder.decode(chunk) which resets decoder state
and breaks multi-byte characters; change to decoder.decode(chunk, { stream: true
}) for each 'data' chunk, and on both the immediate closed path and the 'end'
handler call decoder.decode() (or decoder.decode(undefined)) once to flush any
buffered bytes, enqueue the flushed string if non-empty, then call
this.asyncQueue.end().

@claude
Copy link

claude bot commented Oct 30, 2025

Code Review: Add Concurrency Tests

Thank you for this PR! This is a valuable addition that tests concurrent React Server Component rendering. Here's my detailed review:


Positive Aspects

  1. Excellent test coverage - The PR adds comprehensive tests for concurrent RSC rendering scenarios, which is critical for ensuring thread-safety and isolation
  2. Well-structured helper classes - AsyncQueue and StreamReader are clean abstractions that make the tests readable
  3. Good use of mocking - Using mock-fs to mock the file system is appropriate for unit tests
  4. Documents known bugs - The test labeled [bug] that identifies log leakage is helpful for tracking issues

Issues to Address

Critical Issues

1. Incomplete PR Description ⚠️

The PR description still contains placeholder text. Please update it with:

  • Description of what these tests validate
  • Any bugs discovered during testing
  • Context about why these concurrency tests are needed

2. Missing Checklist Items ⚠️

According to the PR template, you need to:

  • Add/update test to cover these changes ✅ (Done)
  • Update documentation ❌ (Missing)
  • Update CHANGELOG file ❌ (Missing - REQUIRED per CLAUDE.md)

Per CLAUDE.md guidelines:

Add the CHANGELOG entry at the top of the file.

3. Typo in Test Name (serverRenderRSCReactComponent.rsc.test.tsx:97)

test('no logs lekage from outside the component', async () => {

Fix: lekageleakage

4. Typo in Variable Name (removeRSCChunkStack.ts:4)

const santizedHtml = html.split('\n').map(chunkLine => {

Fix: santizedHtmlsanitizedHtml

5. Typo in Console.log (serverRenderRSCReactComponent.rsc.test.tsx:18, 20)

console.log(`[${name}] Before awaitng`);
console.log(`[${name}] After awaitng`);

Fix: awaitngawaiting


Code Quality Issues

6. Potential undefined in Array (removeRSCChunkStack.ts:9-12)

const regexMatch = /(^\d+):\{/.exec(chunkLine)
if (!regexMatch) {
  return;  // Returns undefined - could create sparse array
}

Issue: If regex doesn't match, the function returns undefined, which will create a sparse array with undefined elements. This could cause issues when stringifying.

Suggested fix:

if (!regexMatch) {
  return chunkLine;  // Return original line if pattern doesn't match
}

7. Unnecessary .ts Extensions in Imports (serverRenderRSCReactComponent.rsc.test.tsx:14-15)

import ReactOnRails, { RailsContextWithServerStreamingCapabilities } from '../src/ReactOnRailsRSC.ts';
import removeRSCStackFromAllChunks from './utils/removeRSCStackFromAllChunks.ts'

Issue: TypeScript/Jest typically resolves imports without extensions. Including .ts is unconventional and could cause issues with some build tools.

Suggested fix: Remove .ts extensions from imports.

8. Unused Imports (concurrentRSCPayloadGeneration.rsc.test.tsx:7)

import { PassThrough, Readable, Transform } from 'node:stream';

Issue: Transform is imported but never used.

Suggested fix: Remove unused Transform import.

9. Unused Imports (serverRenderRSCReactComponent.rsc.test.tsx:8-12)

import * as fs from 'fs';
import { pipeline, finished } from 'stream/promises';
import { buildServerRenderer } from 'react-on-rails-rsc/server.node';
import createReactOutput from 'react-on-rails/createReactOutput';

Issue: Only finished is used; fs, pipeline, buildServerRenderer, and createReactOutput are unused.

10. Inconsistent Semicolon Usage (AsyncQueue.ts:33)

let teardown = () => {}  // Missing semicolon

Issue: Inconsistent with the rest of the codebase which uses semicolons.


Performance Considerations

11. Interval Timing in Tests (serverRenderRSCReactComponent.rsc.test.tsx:32)

const intervalId = setInterval(() => {
  console.log(`Interval ${i} at [${name}]`);
  i += 1;
  if (i === 50) {
    clearInterval(intervalId);
    resolve(`Value of name ${name}`);
  }
}, 1);  // 1ms interval for 50 iterations = ~50ms minimum

Issue: The test uses 1ms intervals with 50 iterations. While fine for testing, this could be flaky on slow CI systems.

Suggestion: Consider documenting why these specific values were chosen, or use more deterministic timing.


Best Practice Suggestions

12. Test Isolation with mock-fs

The mock-fs setup is done at module level in both test files. Consider using beforeEach/afterEach for better test isolation:

beforeEach(() => {
  mock({
    [clientManifestPath]: JSON.stringify({...}),
  });
});

afterEach(() => {
  mock.restore();
});

13. Magic Numbers

  • Line 101: expect.assertions(258) - This specific number isn't clearly documented. Add a comment explaining how this number is calculated.
  • Line 130: createParallelRenders(50) - Consider extracting as a named constant with documentation.

14. Variable Shadowing (concurrentRSCPayloadGeneration.rsc.test.tsx:84)

const enqueue = (value: string) => asyncQueues.forEach(asyncQueues => asyncQueues.enqueue(value));

Issue: The parameter name asyncQueues shadows the outer asyncQueues array variable.

Suggested fix:

const enqueue = (value: string) => asyncQueues.forEach(queue => queue.enqueue(value));

Security Considerations

No security issues identified. The code:

  • Uses appropriate sandboxing with mock-fs
  • Doesn't expose sensitive data
  • Properly cleans up resources (afterAll(() => mock.restore()))

Type Safety Note

The type change removing reactClientManifestFileName from RSCRenderParams (types/index.ts:228) is good - it eliminates redundancy since it's already in railsContext. However, ensure this doesn't break any existing usage in the codebase.


Action Items

Before merging:

  1. ✅ Fix all typos mentioned above
  2. ✅ Remove unused imports
  3. ✅ Fix variable shadowing and undefined return issues
  4. ✅ Remove .ts extensions from imports
  5. ✅ Add semicolons consistently
  6. ✅ Update CHANGELOG.md (MANDATORY per CLAUDE.md)
  7. ✅ Complete PR description
  8. ✅ Run bundle exec rubocop and fix any violations (MANDATORY per CLAUDE.md)
  9. ✅ Ensure all files end with newline character (MANDATORY per CLAUDE.md)
  10. ⚠️ Consider addressing the known bug documented in the test or create a follow-up issue

Nice to have:

  • Add comments explaining magic numbers
  • Improve test isolation with proper setup/teardown
  • Document timing choices in async tests

Overall, this is a valuable contribution that significantly improves test coverage for concurrent RSC rendering! Once the issues above are addressed, this will be ready to merge. Great work! 🚀

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx (1)

63-75: Provide a real RailsContextWithServerStreamingCapabilities implementation.

The railsContext mock uses an unsafe as unknown as cast and references the removed reactClientManifestFileName field (line 66). Additionally, it lacks the required getRSCPayloadStream and addPostSSRHook methods from the RailsContextWithServerStreamingCapabilities type. Extract a properly typed mock that implements these methods correctly.

🧹 Nitpick comments (1)
packages/react-on-rails-pro/tests/utils/removeRSCChunkStack.ts (1)

4-4: Fix typo in variable name.

The variable is misspelled as santizedHtml instead of sanitizedHtml.

Apply this diff:

-  const santizedHtml = html.split('\n').map(chunkLine => {
+  const sanitizedHtml = html.split('\n').map(chunkLine => {

And update line 22:

   return JSON.stringify({
     ...parsedJson,
-    html: santizedHtml,
+    html: sanitizedHtml,
   });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between add0cd8 and 891e780.

📒 Files selected for processing (4)
  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx (1 hunks)
  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx (1 hunks)
  • packages/react-on-rails-pro/tests/utils/removeRSCChunkStack.ts (1 hunks)
  • packages/react-on-rails-pro/tests/utils/removeRSCStackFromAllChunks.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/react-on-rails-pro/tests/serverRenderRSCReactComponent.rsc.test.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,jsx,ts,tsx,css,scss,json,yml,yaml,md}

📄 CodeRabbit inference engine (CLAUDE.md)

Prettier is the sole authority for formatting all non-Ruby files; never manually format them

Files:

  • packages/react-on-rails-pro/tests/utils/removeRSCStackFromAllChunks.ts
  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
  • packages/react-on-rails-pro/tests/utils/removeRSCChunkStack.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint for JS/TS code (lint via rake lint or yarn lint)

Files:

  • packages/react-on-rails-pro/tests/utils/removeRSCStackFromAllChunks.ts
  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
  • packages/react-on-rails-pro/tests/utils/removeRSCChunkStack.ts
🧠 Learnings (11)
📓 Common learnings
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1696
File: node_package/src/transformRSCNodeStream.ts:3-31
Timestamp: 2025-04-09T13:02:16.009Z
Learning: In the React Server Components (RSC) stream processing implementation, all chunks end with a newline character (`\n`), so the `lastIncompleteChunk` variable is expected to be empty by the time the stream ends, making a flush handler unnecessary in the Transform stream.
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1696
File: node_package/src/RSCPayloadGenerator.ts:124-168
Timestamp: 2025-06-09T07:58:02.646Z
Learning: In React Server Components (RSC) implementations, explicit error handling in RSC payload generation streams (like in RSCPayloadGenerator.ts) is not needed because errors will propagate to client components that fetch the RSC payloads (such as RSCRoute components). React's built-in error handling mechanisms and ErrorBoundary components are sufficient for handling these errors at the component level, which allows for better user experience with appropriate error UI fallbacks.
📚 Learning: 2024-12-12T13:07:09.929Z
Learnt from: alexeyr-ci
PR: shakacode/react_on_rails#1644
File: node_package/src/ReactOnRailsRSC.ts:87-87
Timestamp: 2024-12-12T13:07:09.929Z
Learning: When handling errors in 'node_package/src/ReactOnRailsRSC.ts', include the error stack in error messages in development and test environments to aid debugging.

Applied to files:

  • packages/react-on-rails-pro/tests/utils/removeRSCStackFromAllChunks.ts
  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
  • packages/react-on-rails-pro/tests/utils/removeRSCChunkStack.ts
📚 Learning: 2025-04-09T13:02:16.009Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1696
File: node_package/src/transformRSCNodeStream.ts:3-31
Timestamp: 2025-04-09T13:02:16.009Z
Learning: In the React Server Components (RSC) stream processing implementation, all chunks end with a newline character (`\n`), so the `lastIncompleteChunk` variable is expected to be empty by the time the stream ends, making a flush handler unnecessary in the Transform stream.

Applied to files:

  • packages/react-on-rails-pro/tests/utils/removeRSCStackFromAllChunks.ts
  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
  • packages/react-on-rails-pro/tests/utils/removeRSCChunkStack.ts
📚 Learning: 2025-06-09T07:58:02.646Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1696
File: node_package/src/RSCPayloadGenerator.ts:124-168
Timestamp: 2025-06-09T07:58:02.646Z
Learning: In React Server Components (RSC) implementations, explicit error handling in RSC payload generation streams (like in RSCPayloadGenerator.ts) is not needed because errors will propagate to client components that fetch the RSC payloads (such as RSCRoute components). React's built-in error handling mechanisms and ErrorBoundary components are sufficient for handling these errors at the component level, which allows for better user experience with appropriate error UI fallbacks.

Applied to files:

  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.

Applied to files:

  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
📚 Learning: 2025-07-08T05:57:29.630Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1745
File: node_package/src/RSCRequestTracker.ts:8-14
Timestamp: 2025-07-08T05:57:29.630Z
Learning: The global `generateRSCPayload` function in React on Rails Pro (RORP) is provided by the framework during rendering requests, not implemented in application code. The `declare global` statements are used to document the expected interface that RORP will inject at runtime.

Applied to files:

  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
  • packages/react-on-rails-pro/tests/utils/removeRSCChunkStack.ts
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation is handled in deeper level calls of the React on Rails Pro codebase, so it doesn't need to be validated again in the `rsc_payload_react_component` helper method.

Applied to files:

  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
📚 Learning: 2024-10-08T20:53:47.076Z
Learnt from: theforestvn88
PR: shakacode/react_on_rails#1620
File: spec/dummy/client/app/startup/HelloTurboStream.jsx:3-3
Timestamp: 2024-10-08T20:53:47.076Z
Learning: The `RailsContext` import in `spec/dummy/client/app/startup/HelloTurboStream.jsx` is used later in the project, as clarified by the user theforestvn88.

Applied to files:

  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
📚 Learning: 2025-10-23T17:22:01.074Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.

Applied to files:

  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.

Applied to files:

  • packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx
📚 Learning: 2025-04-09T12:56:10.756Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1696
File: node_package/src/RSCPayloadContainer.ts:0-0
Timestamp: 2025-04-09T12:56:10.756Z
Learning: In the react_on_rails codebase, RSC payloads are already stringified using `JSON.stringify()` before being processed by the `escapeScript` function, which handles escaping of special characters. The function only needs to handle specific HTML markers like comments and closing script tags.

Applied to files:

  • packages/react-on-rails-pro/tests/utils/removeRSCChunkStack.ts
🧬 Code graph analysis (1)
packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx (4)
packages/react-on-rails/src/types/index.ts (1)
  • RailsContextWithServerStreamingCapabilities (65-68)
packages/react-on-rails-pro/tests/testUtils.js (1)
  • stream (14-21)
packages/react-on-rails-pro/tests/AsyncQueue.ts (1)
  • enqueue (8-18)
packages/react-on-rails-pro/tests/StreamReader.ts (1)
  • nextChunk (28-30)
🪛 GitHub Actions: JS unit tests for Renderer package
packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx

[error] 1-1: Test suite failed to run. ENOENT, no such file or directory '/home/runner/work/react_on_rails/react_on_rails/node_modules/jest-worker/build/index.js'

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: lint-js-and-ruby
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: rspec-package-tests (3.4, latest)
  • GitHub Check: rspec-package-tests (3.2, latest)
  • GitHub Check: rspec-package-tests (3.2, minimum)
  • GitHub Check: rspec-package-tests (3.4, minimum)
  • GitHub Check: build-dummy-app-webpack-test-bundles (3.4, 22)
  • GitHub Check: build-dummy-app-webpack-test-bundles (3.2, 20)
  • GitHub Check: build
  • GitHub Check: claude-review
🔇 Additional comments (5)
packages/react-on-rails-pro/tests/concurrentRSCPayloadGeneration.rsc.test.tsx (5)

1-29: LGTM! Test setup is well-structured.

The Jest environment configuration, mock filesystem setup, and cleanup are correctly implemented.


31-59: LGTM! Test components are well-designed.

The AsyncQueueItem and AsyncQueueContainer components effectively test concurrent RSC streaming with proper Suspense boundaries and async queue integration.


77-98: LGTM! Parallel rendering helper is well-implemented.

The createParallelRenders function provides a clean abstraction for testing concurrent RSC streams with proper reader management and assertion helpers.


100-141: LGTM! Comprehensive test coverage for concurrent RSC streaming.

The test effectively validates both single-stream and concurrent (50 parallel) RSC payload generation with proper assertions.


1-1: Pipeline failure appears to be a CI environment issue.

The test suite failure about missing jest-worker/build/index.js suggests a dependency installation problem in the CI environment rather than an issue with the test code itself. Consider re-running the CI pipeline or checking the dependency installation step.

Comment on lines +4 to +18
const santizedHtml = html.split('\n').map(chunkLine => {
if (!chunkLine.includes('"stack":')) {
return chunkLine;
}

const regexMatch = /(^\d+):\{/.exec(chunkLine)
if (!regexMatch) {
return;
}

const chunkJsonString = chunkLine.slice(chunkLine.indexOf('{'));
const chunkJson = JSON.parse(chunkJsonString);
delete chunkJson.stack;
return `${regexMatch[1]}:${JSON.stringify(chunkJson)}`
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Return original line instead of undefined.

When the regex doesn't match on line 10, the function returns undefined on line 11. This will cause the array to contain undefined values, which will be stringified as "undefined" when reconstructing the JSON, corrupting the output.

Apply this diff to return the original line instead:

   const regexMatch = /(^\d+):\{/.exec(chunkLine)
   if (!regexMatch) {
-    return;
+    return chunkLine;
   }
🤖 Prompt for AI Agents
In packages/react-on-rails-pro/tests/utils/removeRSCChunkStack.ts around lines 4
to 18, the map callback returns undefined when the regex doesn't match (line
11), which inserts undefined into the result array and corrupts output; change
that early return to return the original chunkLine (i.e., replace `return;` with
`return chunkLine;`) so non-matching lines are preserved unchanged.

Comment on lines +3 to +7
const removeRSCStackFromAllChunks = (allChunks: string) => {
return allChunks.split('\n')
.map((chunk) => chunk.trim().length > 0 ? removeRSCChunkStack(chunk) : chunk)
.join('\n');
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Handle undefined return from removeRSCChunkStack.

The removeRSCChunkStack function can return undefined (see line 11 in packages/react-on-rails-pro/tests/utils/removeRSCChunkStack.ts), which would cause .join('\n') to convert it to the string "undefined". This will corrupt the output.

Apply this diff to filter out undefined values:

 const removeRSCStackFromAllChunks = (allChunks: string) => {
   return allChunks.split('\n')
-  .map((chunk) => chunk.trim().length > 0 ? removeRSCChunkStack(chunk) : chunk)
+  .map((chunk) => chunk.trim().length > 0 ? removeRSCChunkStack(chunk) : chunk)
+  .filter((chunk): chunk is string => chunk !== undefined)
   .join('\n');
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const removeRSCStackFromAllChunks = (allChunks: string) => {
return allChunks.split('\n')
.map((chunk) => chunk.trim().length > 0 ? removeRSCChunkStack(chunk) : chunk)
.join('\n');
}
const removeRSCStackFromAllChunks = (allChunks: string) => {
return allChunks.split('\n')
.map((chunk) => chunk.trim().length > 0 ? removeRSCChunkStack(chunk) : chunk)
.filter((chunk): chunk is string => chunk !== undefined)
.join('\n');
}
🤖 Prompt for AI Agents
packages/react-on-rails-pro/tests/utils/removeRSCStackFromAllChunks.ts around
lines 3 to 7: the current map can produce undefined values from
removeRSCChunkStack which will become the string "undefined" when joined; update
the pipeline to filter out undefined results before joining (e.g. map(...), then
.filter((s): s is string => s !== undefined) or use flatMap to only return
defined strings) so only real chunk strings are joined with '\n'.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 11

🧹 Nitpick comments (1)
react_on_rails_pro/spec/dummy/client/app/utils/redisReceiver.ts (1)

58-61: Connection serialization logic is correct.

The implementation prevents concurrent connect() calls by reusing a shared connection promise. This resolves potential race conditions.

Consider clearing connectionPromise after successful connection for clarity:

     if (!connectionPromise) {
       connectionPromise = sharedRedisClient.connect();
     }
     await connectionPromise;
+    connectionPromise = undefined;
     isClientConnected = true;

This is optional since the isClientConnected flag already prevents reuse of the stale promise.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 891e780 and d6e69b0.

📒 Files selected for processing (4)
  • react_on_rails_pro/packages/node-renderer/tests/concurrentHtmlStreaming.test.ts (1 hunks)
  • react_on_rails_pro/packages/node-renderer/tests/htmlStreaming.test.js (1 hunks)
  • react_on_rails_pro/packages/node-renderer/tests/httpRequestUtils.ts (1 hunks)
  • react_on_rails_pro/spec/dummy/client/app/utils/redisReceiver.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,jsx,ts,tsx,css,scss,json,yml,yaml,md}

📄 CodeRabbit inference engine (CLAUDE.md)

Prettier is the sole authority for formatting all non-Ruby files; never manually format them

Files:

  • react_on_rails_pro/spec/dummy/client/app/utils/redisReceiver.ts
  • react_on_rails_pro/packages/node-renderer/tests/httpRequestUtils.ts
  • react_on_rails_pro/packages/node-renderer/tests/htmlStreaming.test.js
  • react_on_rails_pro/packages/node-renderer/tests/concurrentHtmlStreaming.test.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint for JS/TS code (lint via rake lint or yarn lint)

Files:

  • react_on_rails_pro/spec/dummy/client/app/utils/redisReceiver.ts
  • react_on_rails_pro/packages/node-renderer/tests/httpRequestUtils.ts
  • react_on_rails_pro/packages/node-renderer/tests/htmlStreaming.test.js
  • react_on_rails_pro/packages/node-renderer/tests/concurrentHtmlStreaming.test.ts
🧠 Learnings (3)
📚 Learning: 2025-07-08T05:57:29.630Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1745
File: node_package/src/RSCRequestTracker.ts:8-14
Timestamp: 2025-07-08T05:57:29.630Z
Learning: The global `generateRSCPayload` function in React on Rails Pro (RORP) is provided by the framework during rendering requests, not implemented in application code. The `declare global` statements are used to document the expected interface that RORP will inject at runtime.

Applied to files:

  • react_on_rails_pro/packages/node-renderer/tests/httpRequestUtils.ts
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.

Applied to files:

  • react_on_rails_pro/packages/node-renderer/tests/htmlStreaming.test.js
📚 Learning: 2024-12-12T13:07:09.929Z
Learnt from: alexeyr-ci
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/ReactOnRailsRSC.ts:87-87
Timestamp: 2024-12-12T13:07:09.929Z
Learning: When handling errors in 'node_package/src/ReactOnRailsRSC.ts', include the error stack in error messages in development and test environments to aid debugging.

Applied to files:

  • react_on_rails_pro/packages/node-renderer/tests/concurrentHtmlStreaming.test.ts
🧬 Code graph analysis (2)
react_on_rails_pro/packages/node-renderer/tests/httpRequestUtils.ts (2)
react_on_rails_pro/packages/node-renderer/tests/htmlStreaming.test.js (7)
  • form (22-22)
  • app (7-7)
  • app (23-23)
  • makeRequest (20-87)
  • client (24-24)
  • request (25-29)
  • chunks (32-32)
react_on_rails_pro/packages/node-renderer/tests/helper.ts (1)
  • readRenderingRequest (137-145)
react_on_rails_pro/packages/node-renderer/tests/concurrentHtmlStreaming.test.ts (2)
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/RedisReceiver.jsx (1)
  • value (7-7)
react_on_rails_pro/packages/node-renderer/tests/httpRequestUtils.ts (1)
  • makeRequest (80-166)
🪛 ast-grep (0.39.6)
react_on_rails_pro/packages/node-renderer/tests/concurrentHtmlStreaming.test.ts

[warning] 71-71: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(redisRequestId, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 103-103: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(redisRequestId, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 108-108: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(redisRequestId, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 113-113: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(redisRequestId, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 118-118: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(redisRequestId, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 123-123: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(redisRequestId, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 128-128: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(redisRequestId, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

🪛 GitHub Actions: React on Rails Pro - Lint
react_on_rails_pro/packages/node-renderer/tests/concurrentHtmlStreaming.test.ts

[error] 15-15: Prettier formatting error. Delete the extra space character at column 42.

🪛 GitHub Actions: React on Rails Pro - Package Tests
react_on_rails_pro/packages/node-renderer/tests/htmlStreaming.test.js

[error] 27-27: ReferenceError: SERVER_BUNDLE_TIMESTAMP is not defined.

🪛 GitHub Check: lint-js-and-ruby
react_on_rails_pro/packages/node-renderer/tests/concurrentHtmlStreaming.test.ts

[failure] 60-60:
Replace redisRequestId with (redisRequestId)


[failure] 59-59:
Replace .fill(null) with ⏎····.fill(null)⏎····


[failure] 56-56:
Insert ;


[failure] 55-55:
Unsafe member access .html on an any value


[failure] 55-55:
Unsafe return of a value of type any


[failure] 55-55:
Replace .split("\n").map(chunk·=>·chunk.trim().length·>·0·?·JSON.parse(chunk).html·:·chunk).join("" with ⏎····.split('\n')⏎····.map((chunk)·=>·(chunk.trim().length·>·0·?·JSON.parse(chunk).html·:·chunk))⏎····.join(''


[failure] 43-43:
Replace "Closed·redis" with 'Closed·redis'


[failure] 41-41:
Replace "Closed·app" with 'Closed·app'


[failure] 39-39:
Replace "Closing·app" with 'Closing·app'


[failure] 15-15:
Delete ·

🔇 Additional comments (9)
react_on_rails_pro/packages/node-renderer/tests/httpRequestUtils.ts (3)

9-11: LGTM!

The test timestamp constants are clearly defined with appropriate test values.


13-65: LGTM!

The createForm function correctly assembles a multipart form payload with all necessary components for testing server render requests.


158-166: LGTM!

The return object provides a well-structured API for tests to monitor streaming responses, with proper encapsulation via getBuffer returning a copy of the internal buffer.

react_on_rails_pro/spec/dummy/client/app/utils/redisReceiver.ts (1)

39-39: LGTM! Connection promise tracking added.

This variable enables serialization of concurrent connection attempts, preventing race conditions during Redis client initialization.

react_on_rails_pro/packages/node-renderer/tests/concurrentHtmlStreaming.test.ts (5)

32-44: LGTM! Test lifecycle setup is appropriate.

The test setup properly initializes Redis client and app, with appropriate cleanup in afterAll. The 20-second timeout accommodates potential delays in shutting down the server and closing connections.


46-52: LGTM! Redis helper functions are clear.

These helper functions provide a clean abstraction for sending values to Redis streams in the test.


67-75: RegExp constructed from variable (low risk).

Static analysis warns about constructing RegExp from the variable redisRequestId (lines 72, and similar warnings throughout). While this could theoretically enable ReDoS attacks, redisRequestId is a UUID generated by randomUUID(), so it's controlled and safe.

If you want to be extra defensive, you could escape the UUID:

const escapedId = redisRequestId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
expect(extractHtmlFromChunks(chunk.replace(new RegExp(escapedId, 'g'), '')))

However, since UUIDs have a fixed format with only alphanumeric characters and hyphens, this is not necessary for security.


76-84: LGTM! Helper functions complete the test orchestration.

These functions properly coordinate sending values and waiting for completion across all parallel instances.


134-147: LGTM! Parallel execution test validates concurrent behavior.

The test properly validates that 20 concurrent renders produce the same chunked output as a single render, ensuring thread-safety and correctness of the streaming implementation.

Comment on lines +13 to +30
// const runningPromises: (string | undefined)[] = [];
// let isRunning = false;
// const OldPromise = globalThis.Promise;
// globalThis.Promise = class Promise<T> extends OldPromise<T> {
// constructor(executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void) {
// super(executor); // call native Promise constructor
// if (!isRunning) {
// isRunning = true;
// const stack = new Error().stack;
// runningPromises.push(stack);
// this.then(() => {
// const index = runningPromises.indexOf(stack);
// runningPromises.splice(index, 1);
// });
// isRunning = false;
// }
// }
// };
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove commented-out code.

This commented-out Promise tracking code should be removed before merging. If this debugging utility is needed in the future, it can be retrieved from git history.

Apply this diff:

-// const runningPromises: (string | undefined)[] = [];
-// let isRunning = false;
-// const OldPromise = globalThis.Promise; 
-// globalThis.Promise = class Promise<T> extends OldPromise<T> {
-//   constructor(executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void) {
-//     super(executor); // call native Promise constructor
-//     if (!isRunning) {
-//       isRunning = true;
-//       const stack = new Error().stack;
-//       runningPromises.push(stack);
-//       this.then(() => {
-//         const index = runningPromises.indexOf(stack);
-//         runningPromises.splice(index, 1);
-//       });
-//       isRunning = false;
-//     }
-//   }
-// };
-
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// const runningPromises: (string | undefined)[] = [];
// let isRunning = false;
// const OldPromise = globalThis.Promise;
// globalThis.Promise = class Promise<T> extends OldPromise<T> {
// constructor(executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void) {
// super(executor); // call native Promise constructor
// if (!isRunning) {
// isRunning = true;
// const stack = new Error().stack;
// runningPromises.push(stack);
// this.then(() => {
// const index = runningPromises.indexOf(stack);
// runningPromises.splice(index, 1);
// });
// isRunning = false;
// }
// }
// };
🧰 Tools
🪛 GitHub Actions: React on Rails Pro - Lint

[error] 15-15: Prettier formatting error. Delete the extra space character at column 42.

🪛 GitHub Check: lint-js-and-ruby

[failure] 15-15:
Delete ·

🤖 Prompt for AI Agents
In
react_on_rails_pro/packages/node-renderer/tests/concurrentHtmlStreaming.test.ts
around lines 13 to 30, remove the entire commented-out Promise
tracking/debugging block (the multiple commented lines replacing
globalThis.Promise) so no dead/commented code remains; if any imports or
references were only for that debug snippet, remove them as well and run tests
to confirm nothing else depended on it.

Comment on lines +54 to +56
const extractHtmlFromChunks = (chunks: string) => {
chunks.split("\n").map(chunk => chunk.trim().length > 0 ? JSON.parse(chunk).html : chunk).join("");
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix formatting, type safety, and add semicolon.

This function has multiple issues flagged by static analysis and the linter.

Apply this diff to fix all issues:

 const extractHtmlFromChunks = (chunks: string) => {
-  chunks.split("\n").map(chunk => chunk.trim().length > 0 ? JSON.parse(chunk).html : chunk).join("");
-}
+  return chunks
+    .split('\n')
+    .map((chunk) => (chunk.trim().length > 0 ? (JSON.parse(chunk) as { html: string }).html : chunk))
+    .join('');
+};

This addresses:

  • Missing return statement and semicolon
  • Type safety by casting JSON.parse result
  • Prettier formatting (quotes, spacing, wrapping)

As per coding guidelines

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const extractHtmlFromChunks = (chunks: string) => {
chunks.split("\n").map(chunk => chunk.trim().length > 0 ? JSON.parse(chunk).html : chunk).join("");
}
const extractHtmlFromChunks = (chunks: string) => {
return chunks
.split('\n')
.map((chunk) => (chunk.trim().length > 0 ? (JSON.parse(chunk) as { html: string }).html : chunk))
.join('');
};
🧰 Tools
🪛 GitHub Check: lint-js-and-ruby

[failure] 56-56:
Insert ;


[failure] 55-55:
Unsafe member access .html on an any value


[failure] 55-55:
Unsafe return of a value of type any


[failure] 55-55:
Replace .split("\n").map(chunk·=>·chunk.trim().length·>·0·?·JSON.parse(chunk).html·:·chunk).join("" with ⏎····.split('\n')⏎····.map((chunk)·=>·(chunk.trim().length·>·0·?·JSON.parse(chunk).html·:·chunk))⏎····.join(''

🤖 Prompt for AI Agents
In
react_on_rails_pro/packages/node-renderer/tests/concurrentHtmlStreaming.test.ts
around lines 54 to 56, the helper lacks a return statement and semicolon, is not
type-safe when using JSON.parse, and needs Prettier-friendly formatting; update
the function to return the joined string, add a trailing semicolon, cast the
JSON.parse result to an appropriate typed shape (e.g., { html: string }) before
accessing .html, and reformat spacing/quotes/wrapping to match project Prettier
rules.

Comment on lines +58 to +66
const createParallelRenders = (size: number) => {
const redisRequestIds = Array(size).fill(null).map(() => randomUUID());
const renderRequests = redisRequestIds.map(redisRequestId => {
return makeRequest(app, {
componentName: 'RedisReceiver',
props: { requestId: redisRequestId },
});
});

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix formatting issues in parallel render setup.

The array initialization and mapping have formatting issues flagged by the linter.

Apply this diff:

 const createParallelRenders = (size: number) => {
-  const redisRequestIds = Array(size).fill(null).map(() => randomUUID());
-  const renderRequests = redisRequestIds.map(redisRequestId => {
+  const redisRequestIds = Array(size)
+    .fill(null)
+    .map(() => randomUUID());
+  const renderRequests = redisRequestIds.map((redisRequestId) => {
     return makeRequest(app, {
       componentName: 'RedisReceiver',
       props: { requestId: redisRequestId },
     });
   });

As per coding guidelines

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const createParallelRenders = (size: number) => {
const redisRequestIds = Array(size).fill(null).map(() => randomUUID());
const renderRequests = redisRequestIds.map(redisRequestId => {
return makeRequest(app, {
componentName: 'RedisReceiver',
props: { requestId: redisRequestId },
});
});
const createParallelRenders = (size: number) => {
const redisRequestIds = Array(size)
.fill(null)
.map(() => randomUUID());
const renderRequests = redisRequestIds.map((redisRequestId) => {
return makeRequest(app, {
componentName: 'RedisReceiver',
props: { requestId: redisRequestId },
});
});
🧰 Tools
🪛 GitHub Check: lint-js-and-ruby

[failure] 60-60:
Replace redisRequestId with (redisRequestId)


[failure] 59-59:
Replace .fill(null) with ⏎····.fill(null)⏎····

🤖 Prompt for AI Agents
In
react_on_rails_pro/packages/node-renderer/tests/concurrentHtmlStreaming.test.ts
around lines 58 to 66, the array initialization and mapping are misformatted;
replace the Array(size).fill(null).map(...) pattern with a clearer Array.from({
length: size }, () => randomUUID()) for redisRequestIds, simplify the
renderRequests mapping to a concise arrow callback (no unnecessary
return/blocks), and fix spacing/semicolon placement to match project lint rules
so the function is consistently formatted.

Comment on lines +67 to +75
const expectNextChunk = async (expectedNextChunk: string) => {
const nextChunks = await Promise.all(renderRequests.map(renderRequest => renderRequest.waitForNextChunk()));
nextChunks.forEach((chunk, index) => {
const redisRequestId = redisRequestIds[index]!;
console.log("Asserting Chunk")
expect(extractHtmlFromChunks(chunk.replace(new RegExp(redisRequestId, 'g'), '')))
.toEqual(extractHtmlFromChunks(expectedNextChunk));
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove debug console.log statement.

The console.log("Asserting Chunk") on line 71 should be removed as it's debug output not needed in the test.

Apply this diff:

   const expectNextChunk = async (expectedNextChunk: string) => {
     const nextChunks = await Promise.all(renderRequests.map(renderRequest => renderRequest.waitForNextChunk()));
     nextChunks.forEach((chunk, index) => {
       const redisRequestId = redisRequestIds[index]!;
-      console.log("Asserting Chunk")
       expect(extractHtmlFromChunks(chunk.replace(new RegExp(redisRequestId, 'g'), '')))
         .toEqual(extractHtmlFromChunks(expectedNextChunk));
     });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const expectNextChunk = async (expectedNextChunk: string) => {
const nextChunks = await Promise.all(renderRequests.map(renderRequest => renderRequest.waitForNextChunk()));
nextChunks.forEach((chunk, index) => {
const redisRequestId = redisRequestIds[index]!;
console.log("Asserting Chunk")
expect(extractHtmlFromChunks(chunk.replace(new RegExp(redisRequestId, 'g'), '')))
.toEqual(extractHtmlFromChunks(expectedNextChunk));
});
}
const expectNextChunk = async (expectedNextChunk: string) => {
const nextChunks = await Promise.all(renderRequests.map(renderRequest => renderRequest.waitForNextChunk()));
nextChunks.forEach((chunk, index) => {
const redisRequestId = redisRequestIds[index]!;
expect(extractHtmlFromChunks(chunk.replace(new RegExp(redisRequestId, 'g'), '')))
.toEqual(extractHtmlFromChunks(expectedNextChunk));
});
}
🧰 Tools
🪛 ast-grep (0.39.6)

[warning] 71-71: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(redisRequestId, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

🤖 Prompt for AI Agents
In
react_on_rails_pro/packages/node-renderer/tests/concurrentHtmlStreaming.test.ts
around lines 67 to 75, remove the debug console.log("Asserting Chunk") statement
on line 71 so the test output is not polluted; simply delete that line and keep
the surrounding await and assertions intact.

Comment on lines +93 to +132
test('Happy Path', async () => {
const parallelInstances = 20;
expect.assertions(parallelInstances*7 + 7);
const redisRequestId = randomUUID();
const { waitForNextChunk, finishedPromise, getBuffer } = makeRequest(app, {
componentName: 'RedisReceiver',
props: { requestId: redisRequestId },
});
const chunks: string[] = [];
let chunk = await waitForNextChunk();
expect(chunk).not.toContain('Unique Value');
chunks.push(chunk.replace(new RegExp(redisRequestId, 'g'), ''));

sendRedisItemValue(redisRequestId, 0, 'First Unique Value');
chunk = await waitForNextChunk();
expect(chunk).toContain('First Unique Value');
chunks.push(chunk.replace(new RegExp(redisRequestId, 'g'), ''));

sendRedisItemValue(redisRequestId, 4, 'Fifth Unique Value');
chunk = await waitForNextChunk();
expect(chunk).toContain('Fifth Unique Value');
chunks.push(chunk.replace(new RegExp(redisRequestId, 'g'), ''));

sendRedisItemValue(redisRequestId, 2, 'Third Unique Value');
chunk = await waitForNextChunk();
expect(chunk).toContain('Third Unique Value');
chunks.push(chunk.replace(new RegExp(redisRequestId, 'g'), ''));

sendRedisItemValue(redisRequestId, 1, 'Second Unique Value');
chunk = await waitForNextChunk();
expect(chunk).toContain('Second Unique Value');
chunks.push(chunk.replace(new RegExp(redisRequestId, 'g'), ''));

sendRedisItemValue(redisRequestId, 3, 'Forth Unique Value');
chunk = await waitForNextChunk();
expect(chunk).toContain('Forth Unique Value');
chunks.push(chunk.replace(new RegExp(redisRequestId, 'g'), ''));

await finishedPromise;
expect(getBuffer).toHaveLength(0);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix critical bug: missing function call parentheses.

Line 132 checks getBuffer directly instead of calling it. This will always pass since it's checking the function reference, not the buffer contents.

Apply this diff:

   await finishedPromise;
-  expect(getBuffer).toHaveLength(0);
+  expect(getBuffer()).toHaveLength(0);

This is consistent with line 83 which correctly calls getBuffer():

renderRequests.forEach(renderRequest => expect(renderRequest.getBuffer()).toHaveLength(0));
🧰 Tools
🪛 ast-grep (0.39.6)

[warning] 103-103: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(redisRequestId, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 108-108: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(redisRequestId, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 113-113: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(redisRequestId, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 118-118: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(redisRequestId, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 123-123: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(redisRequestId, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 128-128: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(redisRequestId, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

🤖 Prompt for AI Agents
In
react_on_rails_pro/packages/node-renderer/tests/concurrentHtmlStreaming.test.ts
around lines 93 to 132, the final assertion checks the getBuffer function
reference instead of its result; update the assertion to call getBuffer() so it
asserts the buffer length (i.e., replace expect(getBuffer).toHaveLength(0) with
expect(getBuffer()).toHaveLength(0)).

Comment on lines +67 to +78
const getAppUrl = (app: ReturnType<typeof buildApp>) => {
const addresssInfo = app.server.address();
if (!addresssInfo) {
throw new Error('The app has no address, ensure to run the app before running tests');
}

if (typeof addresssInfo === 'string') {
return addresssInfo;
}

return `http://localhost:${addresssInfo.port}`;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix typo in variable name.

Line 68 has a typo: addresssInfo should be addressInfo (two s's, not three).

Apply this diff:

-  const addresssInfo = app.server.address();
-  if (!addresssInfo) {
+  const addressInfo = app.server.address();
+  if (!addressInfo) {
     throw new Error('The app has no address, ensure to run the app before running tests');
   }
 
-  if (typeof addresssInfo === 'string') {
-    return addresssInfo;
+  if (typeof addressInfo === 'string') {
+    return addressInfo;
   }
 
-  return `http://localhost:${addresssInfo.port}`;
+  return `http://localhost:${addressInfo.port}`;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const getAppUrl = (app: ReturnType<typeof buildApp>) => {
const addresssInfo = app.server.address();
if (!addresssInfo) {
throw new Error('The app has no address, ensure to run the app before running tests');
}
if (typeof addresssInfo === 'string') {
return addresssInfo;
}
return `http://localhost:${addresssInfo.port}`;
};
const getAppUrl = (app: ReturnType<typeof buildApp>) => {
const addressInfo = app.server.address();
if (!addressInfo) {
throw new Error('The app has no address, ensure to run the app before running tests');
}
if (typeof addressInfo === 'string') {
return addressInfo;
}
return `http://localhost:${addressInfo.port}`;
};
🤖 Prompt for AI Agents
In react_on_rails_pro/packages/node-renderer/tests/httpRequestUtils.ts around
lines 67 to 78, there's a typo: the variable is declared as addresssInfo (three
s's) but should be addressInfo; rename all occurrences of addresssInfo to
addressInfo so the variable name is consistent (declaration, null check, typeof
check, and property access) and ensure references compile.

Comment on lines +127 to +131
const rejectPendingChunkPromise = () => {
if (rejectChunksPromise && buffer.length === 0) {
rejectChunksPromise('Request already eneded');
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix typo in error message.

Line 129 has a typo: "Request already eneded" should be "Request already ended".

Apply this diff:

   const rejectPendingChunkPromise = () => {
     if (rejectChunksPromise && buffer.length === 0) {
-      rejectChunksPromise('Request already eneded');
+      rejectChunksPromise('Request already ended');
     }
   };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const rejectPendingChunkPromise = () => {
if (rejectChunksPromise && buffer.length === 0) {
rejectChunksPromise('Request already eneded');
}
};
const rejectPendingChunkPromise = () => {
if (rejectChunksPromise && buffer.length === 0) {
rejectChunksPromise('Request already ended');
}
};
🤖 Prompt for AI Agents
In react_on_rails_pro/packages/node-renderer/tests/httpRequestUtils.ts around
lines 127 to 131, fix the typo in the error message inside
rejectPendingChunkPromise: change "Request already eneded" to "Request already
ended" so the error string is spelled correctly.

Comment on lines +133 to +144
const finishedPromise = new Promise<void>((resolve, reject) => {
request.on('end', () => {
client.destroy();
resolve();
rejectPendingChunkPromise();
});
request.on('error', (err) => {
client.destroy();
reject(err);
rejectPendingChunkPromise();
});
}).then(() => console.log("Finished Request"));
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove debug console.log statement.

Line 144 contains a console.log statement that should be removed from production test utility code.

Apply this diff:

     request.on('error', (err) => {
       client.destroy();
       reject(err);
       rejectPendingChunkPromise();
     });
-  }).then(() => console.log("Finished Request"));
+  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const finishedPromise = new Promise<void>((resolve, reject) => {
request.on('end', () => {
client.destroy();
resolve();
rejectPendingChunkPromise();
});
request.on('error', (err) => {
client.destroy();
reject(err);
rejectPendingChunkPromise();
});
}).then(() => console.log("Finished Request"));
const finishedPromise = new Promise<void>((resolve, reject) => {
request.on('end', () => {
client.destroy();
resolve();
rejectPendingChunkPromise();
});
request.on('error', (err) => {
client.destroy();
reject(err);
rejectPendingChunkPromise();
});
});
🤖 Prompt for AI Agents
In react_on_rails_pro/packages/node-renderer/tests/httpRequestUtils.ts around
lines 133 to 144, remove the debug console.log("Finished Request") from the
finishedPromise .then() chain so the test utility doesn't print to stdout;
update the promise chain to simply return the promise (or remove the .then()
entirely) and ensure there are no other stray debug console calls left in this
file.

Comment on lines +146 to +156
const waitForNextChunk = () =>
new Promise<string>((resolve, reject) => {
if (client.closed && buffer.length === 0) {
reject('Request already eneded');
}
resolveChunksPromise = resolve;
rejectChunksPromise = reject;
if (buffer.length > 0) {
scheduleResolveChunkPromise();
}
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix typo in error message.

Line 149 has a typo: "Request already eneded" should be "Request already ended" (same typo as Line 129).

Apply this diff:

   const waitForNextChunk = () =>
     new Promise<string>((resolve, reject) => {
       if (client.closed && buffer.length === 0) {
-        reject('Request already eneded');
+        reject('Request already ended');
       }
       resolveChunksPromise = resolve;
       rejectChunksPromise = reject;
       if (buffer.length > 0) {
         scheduleResolveChunkPromise();
       }
     });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const waitForNextChunk = () =>
new Promise<string>((resolve, reject) => {
if (client.closed && buffer.length === 0) {
reject('Request already eneded');
}
resolveChunksPromise = resolve;
rejectChunksPromise = reject;
if (buffer.length > 0) {
scheduleResolveChunkPromise();
}
});
const waitForNextChunk = () =>
new Promise<string>((resolve, reject) => {
if (client.closed && buffer.length === 0) {
reject('Request already ended');
}
resolveChunksPromise = resolve;
rejectChunksPromise = reject;
if (buffer.length > 0) {
scheduleResolveChunkPromise();
}
});
🤖 Prompt for AI Agents
In react_on_rails_pro/packages/node-renderer/tests/httpRequestUtils.ts around
lines 146 to 156, fix the typo in the rejection message ("Request already
eneded") by changing it to "Request already ended" to match the same message
used at line 129; update only the string literal in the reject(...) call.

async function closeRedisClient() {
if (sharedRedisClient && isClientConnected) {
await sharedRedisClient.quit();
const currendRedisClient = sharedRedisClient;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix the typo in variable name.

The variable name has a typo: currendRedisClient should be currentRedisClient.

Apply this diff:

-    const currendRedisClient = sharedRedisClient;
+    const currentRedisClient = sharedRedisClient;
     sharedRedisClient = null;
     connectionPromise = undefined;

-    await currendRedisClient.quit();
+    await currentRedisClient.quit();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const currendRedisClient = sharedRedisClient;
const currentRedisClient = sharedRedisClient;
sharedRedisClient = null;
connectionPromise = undefined;
await currentRedisClient.quit();
🤖 Prompt for AI Agents
In react_on_rails_pro/spec/dummy/client/app/utils/redisReceiver.ts around line
73, there is a typo in the variable name `currendRedisClient`; rename it to
`currentRedisClient` and update all references/usages in this file to use the
corrected name so identifiers remain consistent and compile without errors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants