Skip to content

Add Async Props documentation with animated SVG diagrams#2265

Open
AbanoubGhadban wants to merge 58 commits intoupcoming-v16.3.0from
async-props-documentation
Open

Add Async Props documentation with animated SVG diagrams#2265
AbanoubGhadban wants to merge 58 commits intoupcoming-v16.3.0from
async-props-documentation

Conversation

@AbanoubGhadban
Copy link
Copy Markdown
Collaborator

Summary

  • Adds comprehensive documentation for the Async Props feature
  • Includes 4 static SVG diagrams explaining core concepts
  • Includes 12 animated SVG diagrams with SMIL animations covering:
    • Streaming flow visualization
    • Suspense boundaries and resolution
    • Waterfall vs parallel fetching comparison
    • Hydration process
    • Error handling isolation
    • Full request lifecycle
    • Server vs client components
    • User experience comparison

Documentation Structure

docs/async-props/
├── README.md           # Overview and quick start
├── how-it-works.md     # Deep dive into streaming architecture
├── api-reference.md    # Complete API reference
├── advanced-usage.md   # Error handling, caching, patterns
└── images/
    ├── *.svg           # Static diagrams
    └── animated/*.svg  # Animated SMIL diagrams

Test plan

  • Verify SVG diagrams render correctly in documentation
  • Review documentation content for accuracy
  • Consider converting animated SVGs to GIFs for GitHub compatibility

🤖 Generated with Claude Code

- Introduced `IncrementalRenderRequestManager` to handle streaming NDJSON requests, managing state and processing of incremental render requests.
- Added `validateBundlesExist` utility function to check for the existence of required bundles, improving error handling for missing assets.
- Refactored the incremental render endpoint to utilize the new request manager, enhancing the response lifecycle and error management.
- Updated tests to cover scenarios for missing bundles and validate the new request handling logic.
- Replaced the `IncrementalRenderRequestManager` with `handleIncrementalRenderStream` to manage streaming NDJSON requests more efficiently.
- Enhanced error handling and validation during the rendering process.
- Updated the `run` function to utilize the new stream handler, improving the response lifecycle and overall performance.
- Removed the deprecated `IncrementalRenderRequestManager` class to streamline the codebase.
- Introduced improved error handling for malformed JSON chunks during the incremental rendering process.
- Added logging and reporting for errors in subsequent chunks while allowing processing to continue.
- Updated tests to verify behavior for malformed JSON in both initial and update chunks, ensuring robust error management.
…inability

- Introduced helper functions to reduce redundancy in test setup, including `getServerAddress`, `createHttpRequest`, and `createInitialObject`.
- Streamlined the handling of HTTP requests and responses in tests, enhancing clarity and organization.
- Updated tests to utilize new helper functions, ensuring consistent structure and easier future modifications.
- Replaced inline wait functions with a new `waitFor` utility to improve test reliability and readability.
- Updated tests to utilize `waitFor` for asynchronous expectations, ensuring proper handling of processing times.
- Simplified the test structure by removing redundant wait logic, enhancing maintainability.
…processing

- Introduced `createBasicTestSetup` and `createStreamingTestSetup` helper functions to streamline test initialization and improve readability.
- Added `sendChunksAndWaitForProcessing` to handle chunk sending and processing verification, reducing redundancy in test logic.
- Updated existing tests to utilize these new helpers, enhancing maintainability and clarity in the test structure.
- Added detailed error reporting in the `waitFor` function to include the last encountered error message when a timeout occurs.
- Refactored the `createStreamingResponsePromise` function to improve clarity and maintainability by renaming variables and returning received chunks alongside the promise.
- Updated tests to utilize the new structure, ensuring robust handling of streaming responses and error scenarios.
…ment

- Removed unnecessary bundle validation checks from the incremental render request flow.
- Enhanced the `handleIncrementalRenderRequest` function to directly call `handleRenderRequest`, streamlining the rendering process.
- Updated the `IncrementalRenderInitialRequest` type to support a more flexible structure for dependency timestamps.
- Improved error handling to capture unexpected errors during the rendering process, ensuring robust responses.
- Added cleanup logic in tests to restore mocks after each test case.
- Removed individual protocol version and authentication checks from the request handling flow.
- Introduced a new `performRequestPrechecks` function to streamline the validation process for incoming requests.
- Updated the `authenticate` and `checkProtocolVersion` functions to accept request bodies directly, enhancing modularity.
- Improved error handling by ensuring consistent response structures across precheck validations.
- Updated the `/upload-assets` endpoint to differentiate between assets and bundles, allowing for more flexible uploads.
- Introduced logic to extract bundles prefixed with 'bundle_' and handle them separately.
- Integrated the `handleNewBundlesProvided` function to manage the processing of new bundles.
- Added comprehensive tests to verify the correct handling of uploads with various combinations of assets and bundles, including edge cases for empty requests and duplicate bundle hashes.
- Added tests to verify directory structure and file presence for uploaded bundles and assets.
- Implemented checks for scenarios with empty requests and duplicate bundle hashes, ensuring correct behavior without overwriting existing files.
- Improved coverage of the `/upload-assets` endpoint to handle various edge cases effectively.
- Implemented a new test case for the `/upload-assets` endpoint to verify that bundles are correctly placed in their own hash directories rather than the targetBundles directory.
- Ensured that the test checks for the existence of the bundle in the appropriate directory and confirms that the target bundle directory remains empty, enhancing coverage for asset upload scenarios.
- Implemented a suite of tests for the `/bundles/:bundleTimestamp/incremental-render/:renderRequestDigest` endpoint to verify successful rendering under various conditions, including pre-uploaded bundles and assets.
- Added scenarios to test failure cases, such as missing bundles, incorrect passwords, and invalid JSON payloads.
- Enhanced coverage for handling multiple dependency bundles and processing NDJSON chunks, ensuring robust error management and response validation.
- Simplified test structure by introducing helper functions to reduce code duplication for creating worker apps and uploading bundles.
- Improved test cases for the `/bundles/:bundleTimestamp/incremental-render/:renderRequestDigest` endpoint, ensuring robust validation of successful renders and error handling for various scenarios.
- Added tests for handling invalid JSON and missing required fields, enhancing coverage for edge cases in the rendering process.
- Updated tests to ensure proper handling of multiple dependency bundles and improved response validation for different payload conditions.
- Replaced the `runInVM` function with a new `ExecutionContext` class to manage VM contexts more effectively.
- Updated the `handleRenderRequest` function to utilize the new `ExecutionContext`, improving the handling of rendering requests.
- Enhanced error management by introducing `VMContextNotFoundError` for better clarity when VM contexts are missing.
- Refactored tests to align with the new execution context structure, ensuring consistent behavior across rendering scenarios.
…andling

- Updated the parameters for the `runOnOtherBundle` function to ensure correct execution order.
- Introduced a reference to `globalThis.runOnOtherBundle` in the server rendering code for better accessibility.
- Enhanced the test fixture to align with the changes in the global context, ensuring consistent behavior across rendering requests.
- Introduced `IncrementalRenderSink` type to manage streaming updates more effectively.
- Updated `handleIncrementalRenderRequest` to return an optional sink and handle execution context errors gracefully.
- Refactored the `run` function to utilize the new sink for processing updates, enhancing error logging for unexpected chunks.
- Simplified test setup by removing unused sink methods, ensuring tests focus on relevant functionality.
- Updated the `setResponse` call in the `run` function to correctly use `result.response`.
- Expanded the incremental render tests to cover new scenarios, including basic updates, multi-bundle interactions, and error handling for malformed update chunks.
- Introduced new helper functions in test fixtures to streamline the creation of async values and streams, enhancing the robustness of the tests.
- Improved the secondary bundle's functionality to support async value resolution and streaming, ensuring consistent behavior across bundles.
normalized_value = raw_value.strip.downcase
return PRERENDER_OVERRIDE_VALUES[normalized_value] if PRERENDER_OVERRIDE_VALUES.key?(normalized_value)

Rails.logger.warn(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Rails.logger.warn is called while PRERENDER_OVERRIDE_CACHE_MUTEX is held. Rails logger is thread-safe in isolation, but calling any external logger inside a mutex can cause a deadlock if the logger itself ever acquires a lock that another thread holds while also waiting for PRERENDER_OVERRIDE_CACHE_MUTEX.

In practice this is very unlikely here since the mutex is only held briefly and Rails' logger doesn't call back into prerender_env_override. But the risk can be eliminated entirely by extracting the warn call to outside the synchronize block (build the log message inside, log it outside). Given the value is cached after the first parse, this is a one-time cost anyway.

@@ -0,0 +1,123 @@
# Async Props: Streaming Server-Side Rendering
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Async Props is a React on Rails Pro feature requiring the Node renderer, but this documentation lives in docs/async-props/ (alongside OSS docs) rather than docs/pro/. This could confuse OSS users who try to use this feature.

Consider either:

  • Moving to docs/pro/async-props/
  • Adding a prominent Pro-only notice at the top: "⚠️ This feature requires React on Rails Pro."

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 28, 2026

test

Comment thread packages/react-on-rails-pro-node-renderer/src/worker.ts
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 28, 2026

Review

Overall: The PR bundles async props documentation (SVGs, 4 doc pages) with several unrelated runtime changes: a breaking upload protocol migration, an async gem version bump, and a meaningful asset-copy bug fix. The runtime changes are the most review-sensitive part and deserve their own PR history.

Breaking changes

Two breaking changes are documented in the CHANGELOG but worth flagging in review:

  1. async >= 2.29 is now required. Users who pin gem 'async', '~> 2.x' below 2.29 will see a bundler conflict on upgrade.
  2. /upload-assets now requires bundle_<hash> keys. Old Ruby clients sending only targetBundles to a new node renderer receive a 400 immediately. The gem adds targetBundles for backward-compat with old node renderers, but the reverse direction has no grace period -- upgrading the node renderer before the gem will break uploads.

Security: path traversal in extractBundlesAndAssets

The timestamp value is sliced directly from the multipart form key and passed to getBundleDirectory, which calls path.join(serverBundleCachePath, timestamp). path.join does not block traversal sequences, so a key like bundle_../../../tmp/evil would resolve outside the cache directory. The render endpoint URL param has the same pre-existing exposure, but the form-key surface is new here. A simple allowlist regex on the extracted hash (e.g. /^[\w.-]+$/) would close this.

Minor

  • When the key is exactly bundle_ with no suffix, the value is silently discarded (only a log.warn). A client sending bundle_=<file> alongside valid keys would have that file ignored with no error signal.
  • consumer_stream_async: raise if first_chunk_promise.resolved? relies on Async cooperative scheduling guaranteeing no fiber switch between the guard and reject. The comment explains this well -- worth preserving in any future refactors.

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 28, 2026

Test comment posting

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 28, 2026

Review: Async Props Documentation - please see full review in separate comment

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 28, 2026

Review: Async Props Documentation

Thanks for the documentation effort. However, there are several accuracy issues that need to be addressed before this can merge.

CRITICAL: Fictional API Surface

1. useAsyncProp hook does not exist.
advanced-usage.md uses useAsyncProp('users') in four places (lines 126, 201, 301, 323). There is no such hook. The actual API is getReactOnRailsAsyncProp, a function prop injected into Server Components by addAsyncPropsCapabilityToComponentProps(). These are architecturally different - you cannot use a hook in Server Components.

2. async_prop(timeout:, on_error:) options are shown as working code but are not implemented.
advanced-usage.md lines 109-120 present these as functional examples with no caveat. api-reference.md has a disclaimer: 'Per-prop :timeout and :on_error are not implemented in the current release.' The docs directly contradict each other. Examples in advanced-usage.md need to be removed or clearly marked as planned/future API.

3. Several Rails config options in api-reference.md do not exist in ReactOnRailsPro::Configuration:

  • config.node_renderer_timeout (does not exist; actual option is ssr_timeout)
  • config.async_props_default_timeout (does not exist)
  • config.async_props_parallel_limit (does not exist)
  • config.trace_async_props in advanced-usage.md (does not exist)

Verified against react_on_rails_pro/lib/react_on_rails_pro/configuration.rb.

Minor Issues

4. Animated SVGs will not render on GitHub.
SMIL animations are blocked by the GitHub SVG sanitizer. The PR description already flags this. Convert to GIF/WebP or add a note about the limitation.

5. PR scope does not match the title.
The title says 'Add Async Props documentation' but 231 files are changed, including CHANGELOG.md, AGENTS.md, CLAUDE.md, and GitHub Actions workflows. Consider splitting the master-to-main rename changes into a separate PR.

Whats Good

  • Documentation structure (README, how-it-works, api-reference, advanced-usage) is well-organized
  • The NDJSON protocol description in how-it-works.md is accurate and useful
  • The AsyncPropsManager pseudocode is a helpful illustration
  • The CI action rename (ensure-master-docs-safety to ensure-main-docs-safety) is correct

Comment thread docs/async-props/advanced-usage.md Outdated
Comment thread docs/async-props/advanced-usage.md Outdated
Comment thread docs/async-props/advanced-usage.md Outdated
Comment thread docs/async-props/advanced-usage.md Outdated
Comment thread docs/async-props/api-reference.md Outdated
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 28, 2026

PR Review

Scope / Title Mismatch

The title says Add Async Props documentation, but this PR bundles several distinct changes: new async-props docs with animated SVGs, a path-traversal security fix in validateBundleTimestamp, a breaking /upload-assets protocol change (bundle_ keys replace targetBundles), an async gem version bump from >=2.6 to >=2.29, moving copyUploadedAssets outside the per-bundle lock, Async::Variable to Async::Promise migration, master-to-main renames across 20+ files, and a version bump to 16.5.0. Consider splitting out the documentation as a standalone PR.

Security - validateBundleTimestamp: Good fix. The regex blocks path separators and traversal sequences. Test coverage is present and correct. Minor: getRequestBundleFilePath calls validateBundleTimestamp twice on the same input (once inside getBundleDirectory and once directly). The second call is redundant.

Protocol Change - /upload-assets (breaking): The endpoint now rejects requests lacking a bundle_ field. The Ruby side still sends targetBundles for backward compat, but older node renderer versions will get a 400 because they never sent bundle_ keys. The CHANGELOG and TODO in request.rb note this. Please verify the migration docs make clear that gem and node renderer must be upgraded atomically.

Async::Variable to Async::Promise - logic is correct: The resolved? + reject sequence in consumer_stream_async is safe under cooperative scheduling since no fiber switch occurs between the two calls. The contract canary in promise_spec.rb is a nice addition. Eager-loading require async/promise at module load level is the right call.

copyUploadedAssets moved outside the lock: Asset copy now runs after unlock, so two concurrent workers targeting the same bundle directory can copy assets simultaneously. The comment explains this is intentional (overwrite: true, idempotent). That holds for identical files. If two workers upload different assets with the same filename (unlikely but possible), the last write wins silently. Worth confirming this invariant is acceptable.

Documentation quality: The async-props docs are thorough and the animated SVGs are a clear win. The performance table (1800ms to 50ms TTFB/TTI) uses specific numbers that users might treat as benchmarks - consider noting these are illustrative. SMIL animations render in GitHub web UI but not VS Code preview; the test plan GIF conversion item is worth completing before merge.

return path.join(bundleDirectory, filename);
}

const SAFE_BUNDLE_TIMESTAMP_PATTERN = /^(?!\.{1,2}$)[A-Za-z0-9_.-]+$/;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The regex is correct for preventing path traversal — no path separators are allowed so no component can escape serverBundleCachePath. One note: the negative lookahead only rejects the strings "." and ".." exactly. Values like "..foo" pass the check, which is fine (they are safe) but unusual for a timestamp. If you want to be stricter you could anchor the first character: /^[A-Za-z0-9][A-Za-z0-9_.-]*$/, which also removes the need for the lookahead.

Comment on lines 188 to +191
export function getRequestBundleFilePath(bundleTimestamp: string | number) {
const bundleDirectory = getBundleDirectory(bundleTimestamp);
return path.join(bundleDirectory, `${bundleTimestamp}.js`);
const safeBundleTimestamp = validateBundleTimestamp(bundleTimestamp);
return path.join(bundleDirectory, `${safeBundleTimestamp}.js`);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

validateBundleTimestamp is called twice here: once inside getBundleDirectory (line 189) and once directly on line 190. Since getBundleDirectory already validates and returns the safe string, you can reuse its return value:

Suggested change
export function getRequestBundleFilePath(bundleTimestamp: string | number) {
const bundleDirectory = getBundleDirectory(bundleTimestamp);
return path.join(bundleDirectory, `${bundleTimestamp}.js`);
const safeBundleTimestamp = validateBundleTimestamp(bundleTimestamp);
return path.join(bundleDirectory, `${safeBundleTimestamp}.js`);
export function getRequestBundleFilePath(bundleTimestamp: string | number) {
const bundleDirectory = getBundleDirectory(bundleTimestamp);
const safeBundleTimestamp = bundleDirectory.slice(bundleDirectory.lastIndexOf('/') + 1);
return path.join(bundleDirectory, `${safeBundleTimestamp}.js`);

Or more simply, just accept that getBundleDirectory validates and derive the filename from the validated value returned by the first call rather than re-validating.

Comment on lines +133 to +138
// Always copy assets to the bundle directory — even if the bundle was
// already present (e.g., from a prior upload or another worker).
// copyUploadedAssets uses overwrite:true, so this is idempotent.
if (assetsToCopy) {
await copyUploadedAssets(assetsToCopy, bundleDirectory);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Moving asset copy outside the lock is intentional (so assets are always applied even when the bundle was already present), and overwrite: true makes it safe for the normal case. One edge case to confirm: two concurrent workers uploading different assets under the same filename to the same bundle directory will race silently — the last writer wins. This is an acceptable invariant if assets for a given bundle are always content-identical across uploads, but if that assumption can ever be violated a log.info or log.warn noting the overwrite would help with debugging.

Comment on lines +148 to 154
# TODO: targetBundles is only kept for backward compatibility with older node renderers
# (protocol 2.0.0) that require it. The new node renderer derives target directories from
# the bundle_<hash> form keys and ignores this field. Remove at the next breaking version.
# Note: it's not mandatory to keep this until then — users are expected to upgrade the
# node renderer and react_on_rails gem to the same version together — but it's an easy
# backward compatibility safeguard.
form["targetBundles"] = target_bundles
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The backward-compat comment is clear, but note the asymmetry: the Ruby gem sends both targetBundles (for old renderers) and the new bundle_<hash> fields (for the new renderer). An old node renderer that sees bundle_<hash> keys will treat them as unrecognised form fields and silently ignore them while reading targetBundles as before — so the Ruby-new / renderer-old direction is safe. The reverse (Ruby-old / renderer-new) is the breaking direction: old Ruby sends only targetBundles, new renderer rejects with 400. The atomic-upgrade requirement is therefore one-directional: renderer can be upgraded first, gem second, but not gem first. Worth documenting this order explicitly if it is not already in the upgrade guide.

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 28, 2026

PR Review

This PR has a misleading title. The title says "Add Async Props documentation with animated SVG diagrams" but the actual changes are much broader - this is a v16.5.0 release PR containing:

  • Version bump 16.4.0 to 16.5.0 (gem + npm packages)
  • New VersionSynchronizer class for gem/npm version sync
  • REACT_ON_RAILS_PRERENDER_OVERRIDE env-based prerender override with caching
  • New Doctor checks for Pro setup and RSC configuration
  • ProcessManager fix to preserve PORT/SHAKAPACKER_DEV_SERVER_PORT across with_unbundled_env
  • Migration from Async::Variable (deprecated) to Async::Promise
  • /upload-assets protocol change: bundle hash keys replace targetBundles array
  • Path traversal protection in validateBundleTimestamp (security fix)
  • Asset copy ordering fix in handleRenderRequest.ts (idempotent, post-move)
  • Async props documentation + 16 SVG diagrams
  • 232 files changed overall

The PR title/description should accurately reflect this scope.

config_path = "config/initializers/react_on_rails_pro.rb"
return false unless File.exist?(config_path)

File.read(config_path).match?(/server_renderer\s*=\s*["']NodeRenderer["']/)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The regex /server_renderer\s*=\s*["']NodeRenderer["']/ will match commented-out lines (e.g., # config.server_renderer = "NodeRenderer"), producing a false positive. Use a negative lookbehind or filter to commented lines first:

Suggested change
File.read(config_path).match?(/server_renderer\s*=\s*["']NodeRenderer["']/)
File.read(config_path).each_line.any? do |line|
!line.match?(/^\s*#/) && line.match?(/server_renderer\s*=\s*["']NodeRenderer["']/)
end

# so `import ... from 'react-on-rails'` resolves silently — loading the base package instead
# of Pro. Components registered through the base package won't have Pro features (streaming,
# caching, RSC), and may cause "component not registered" errors at runtime.
BASE_PACKAGE_IMPORT_PATTERN = %r{\bfrom\s+['"]react-on-rails(?:/[^'"]*)?['"]}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This pattern will match import statements inside single-line JS comments (// import ReactOnRails from 'react-on-rails'), generating false-positive warnings. The file-level check (content.match?) has no way to distinguish commented imports from live ones. Consider using a line-by-line filter that skips lines starting with // or * before running the regex.

# Returns true if environment was loaded successfully, false otherwise.
def ensure_rails_environment_loaded
return true if @rails_environment_loaded
return false if @rails_environment_attempted
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@rails_environment_attempted is used here but never initialized in initialize (unlike @rails_environment_loaded = false). Ruby treats uninitialized ivar as nil (falsy), so the guard works, but it's inconsistent. Add @rails_environment_attempted = false to initialize for symmetry.

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 28, 2026

Substantive issues

1. pro_initializer_has_node_renderer? matches commented-out config - The regex matches commented-out lines like a disabled config.server_renderer assignment, giving a false positive where check_server_rendering_engine reports Pro uses NodeRenderer even when it is not active.

2. BASE_PACKAGE_IMPORT_PATTERN matches imports inside JS comments - The pattern fires on single-line JS comment import statements, generating spurious warnings for intentionally-commented migration notes in JS/TS files.

3. @rails_environment_attempted not initialized in initialize - @rails_environment_loaded is set to false in initialize, but @rails_environment_attempted is not. Ruby nil is falsy so the guard works, but the inconsistency could confuse future contributors.

4. asset_exists_on_vm_renderer? uses old targetBundles without a compat comment - request.rb line 170 sets form_data targetBundles for the /asset-exists endpoint with no explanation of its compatibility status relative to the new bundle_hash protocol. The upload_assets method has a TODO tracking this; this endpoint does not.

Minor observations - docs/async-props/ references Pro streaming APIs and RSC heavily but lives in the OSS docs/ tree with no Pro-only callout. The animated SVGs use SMIL, which the PR own test plan flags as potentially incompatible with GitHub rendering.

# the bundle_<hash> form keys and ignores this field. Remove at the next breaking version.
# Note: it's not mandatory to keep this until then — users are expected to upgrade the
# node renderer and react_on_rails gem to the same version together — but it's an easy
# backward compatibility safeguard.
form["targetBundles"] = target_bundles

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This still sends targetBundles to the node renderer with no comment explaining its compatibility status relative to the new bundle_<hash> protocol. The upload_assets method (line 148–154) has a TODO tracking backward compatibility, but this endpoint does not. Please add a matching comment here so it doesn't get missed when the deprecated field is eventually removed.

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

Labels

documentation needs-review No final review yet P1 Target this sprint

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants