Harden Inertia request state and SSR lifecycle - #487
Conversation
Replicate provider-boot defaults into a request-local InertiaState and route every state consumer through that owner. Centralize SSR dispatch, keep the resolved page authoritative, and prevent view data from replacing framework protocol data. Make component and directive fallback JSON failures explicit, avoid duplicate page encoding after successful SSR, preserve exact root identifiers and integer prop keys, and cover boot inheritance, sibling isolation, dispatch identity, and rendering equivalence.
Resolve each mutable ScrollProp through its own shallow copy so boot-shared values, sibling requests, and repeated logical paths cannot share cached results or merge metadata. Memoize legitimate null results while retaining retry behavior after exceptions. Preserve falsey helper, merge, once, reset, and diagnostic values; use strict protocol-list membership; normalize numeric keys only where string paths are required; and correct the affected return and owner annotations. Add regressions for nested providers, numeric-looking header collisions, metadata ownership, and null resolution.
Attach the current asset version to mismatch redirects, append and case-insensitively deduplicate Vary on the response actually returned, and preserve existing cache variance through replacement responses. Treat only exact empty-string content as an empty response so zero, streamed, and binary bodies remain intact. Preserve zero-valued error-bag names and use strict redirect-method matching, with coverage for normal responses and every replacement path.
Give the gateway contract and concrete transport one worker identity, retain the reusable cookie-free client, and singleton-reuse the existing page finder cache. Add configurable hot URLs with checked publication fallback and normalize provider container resolution. Validate the exact SSR success shape, normalize remote error metadata, and limit worker backoff to connection and malformed-transport failures while clearing it on proven reachability. Add handler-independent health and shutdown transport behavior with comprehensive success, failure, backoff, identity, cache, and configuration coverage.
Replace process-global PCNTL handlers with the inherited coroutine-scoped signal registry while retaining Node, Bun, and absolute runtime selection. Return failure for unsuccessful child exits instead of reporting every completed process as success. Make the stop command verify server health through the shared transport before shutdown, distinguish refusal from the normal response-less close, and return standard console outcomes. Cover runtime arguments, signal cleanup, child exits, health failures, returned statuses, and closed shutdown connections.
Add concise Laravel-style method docblocks to the exception response and facade extension surfaces without changing behavior or visibility. Complete the missing void return types in the bundle detector tests so the touched package test surface follows the repository typing conventions.
Document runtime selection, configurable hot URLs, transport timeouts, bounded worker backoff, client-rendered fallback, failure events, and throw-on-error behavior in the canonical Vite guide using Laravel-style prose. Keep the package README minimal while recording the public raw-client testing seam and standardizing the upstream reference. Complete the Vite table of contents and keep the starter-kit note attached to the start command.
Capture the final request-state, prop ownership, protocol, SSR transport, command lifecycle, documentation, performance, testing, and compatibility decisions in the focused implementation plan. Update the core routing index and durable ledger with inertia-01 through inertia-23, completed validation and review evidence, rejected machinery, the revalidated support boundary, and the current upstream DevTools surface reserved as the next work unit. Keep the package checklist open until DevTools lands.
…ess-parity-lifecycle # Conflicts: # docs/plans/2026-07-12-0915-framework-coroutine-state-lifecycle-audit-ledger.md
|
Warning Review limit reached
Next review available in: 41 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe Inertia package now centralizes coroutine-local state and SSR dispatch. It tightens response, prop, and transport handling, updates SSR commands and container bindings, and adds documentation plus regression tests for lifecycle isolation and falsey values. ChangesInertia lifecycle and SSR parity
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Request
participant InertiaState
participant ViewComponent
participant HttpGateway
participant SSRServer
Request->>InertiaState: current()
ViewComponent->>InertiaState: set page and dispatchSsr()
InertiaState->>HttpGateway: dispatch SSR request
HttpGateway->>SSRServer: render page
SSRServer-->>HttpGateway: validated head/body or failure
HttpGateway-->>InertiaState: cached response or failure event
InertiaState-->>ViewComponent: SSR response or JSON fallback
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
Greptile SummaryThis PR hardens Inertia request isolation, protocol handling, SSR transport validation, and SSR process lifecycle behavior.
Confidence Score: 4/5The PR is not yet safe to merge because the stop command can still claim success when the shutdown request fails and the SSR server remains running. The separate health request does not prove that the subsequent shutdown request was accepted, yet StopSsr swallows every transport exception and immediately reports that the server stopped. Files Needing Attention: src/inertia/src/Commands/StopSsr.php; tests/Inertia/Commands/StopSsrTest.php
|
| Filename | Overview |
|---|---|
| src/inertia/src/Commands/StopSsr.php | Adds a health preflight and gateway shutdown orchestration, but still reports success for all shutdown transport failures. |
| src/inertia/src/Ssr/HttpGateway.php | Adds validated SSR response handling, transport-specific backoff, hot-file fallback, health checks, and a reusable-client shutdown request. |
| src/inertia/src/InertiaState.php | Introduces a provider-boot baseline replicated into coroutine-local request state. |
| src/inertia/src/PropsResolver.php | Isolates mutable scroll-prop resolution and corrects numeric prop-path handling. |
| src/inertia/src/Middleware.php | Corrects returned-response variance, version headers, empty-response detection, and strict protocol boundaries. |
| tests/Inertia/Commands/StopSsrTest.php | Covers health and shutdown outcomes but incorrectly treats every shutdown transport exception as a successful response-less close. |
Reviews (2): Last reviewed commit: "test(inertia): clarify malformed respons..." | Re-trigger Greptile
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
src/inertia/src/InertiaServiceProvider.php (2)
28-53: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winType all changed container factory callback parameters.
The production and test bindings omit native types for container callback parameters. Verify the container callback contract, then declare the supported types.
src/inertia/src/InertiaServiceProvider.php#L28-L53: type the$appparameters in bothsingleton()factory callbacks.tests/Inertia/Commands/StartSsrTest.php#L30-L35: type the factory callback parameters used bybind(Process::class, ...).As per coding guidelines, “use native types for parameters, return values, and properties wherever permitted.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/inertia/src/InertiaServiceProvider.php` around lines 28 - 53, Type the container factory callback parameters according to the supported container callback contract. In src/inertia/src/InertiaServiceProvider.php lines 28-53, add native types to the $app parameters of both singleton factory callbacks; in tests/Inertia/Commands/StartSsrTest.php lines 30-35, type the factory callback parameters used by bind(Process::class, ...). Preserve the existing binding behavior.Source: Coding guidelines
28-31: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winType the container factory callback parameters.
fn ($app)andfunction ($app)in the singleton factory callbacks should have their container parameters typed. Use the supported container type, and useApplicationif this service provider receives the application instance from Hypervel’sServiceProvider::app.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/inertia/src/InertiaServiceProvider.php` around lines 28 - 31, Update the singleton factory callback in InertiaServiceProvider to type its $app parameter with the supported container type, using Application if ServiceProvider::app provides that instance. Preserve the existing Gateway::class binding and HttpGateway::class resolution.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/inertia/src/Commands/StopSsr.php`:
- Around line 36-45: Update the TransferException handling in the StopSsr
command’s gateway->shutdown() flow so transport failures are not automatically
treated as successful shutdowns. After the expected connection close, verify the
SSR server is unhealthy before returning success; otherwise report the exception
through the command error path and return self::FAILURE, while preserving the
existing handling for a false shutdown result.
In `@src/inertia/src/Inertia.php`:
- Around line 48-50: Update the PHPDoc description for getFacadeAccessor() to
state that it returns the Inertia facade accessor or service binding, replacing
the inaccurate description about a registered component name.
In `@tests/Inertia/HttpGatewayTest.php`:
- Around line 182-205: Extend
testMalformedSuccessDispatchesFailureAndHonorsThrowOnError with a separate
disabled-throwing case: reset the gateway state, set inertia.ssr.throw_on_error
to false, configure a malformed successful SSR response, and assert dispatch
returns null without throwing. Avoid relying on the existing backoff-active
second dispatch, and preserve the current throwing assertion.
- Around line 247-251: Update the listed hot-file tests to configure Vite with a
test-owned disposable path via Vite::useHotFile(...) before createHotFile(...),
rather than writing to the default public hot path. Track and delete only that
temporary path during teardown, preserving each test’s existing assertions and
hot-file behavior.
---
Nitpick comments:
In `@src/inertia/src/InertiaServiceProvider.php`:
- Around line 28-53: Type the container factory callback parameters according to
the supported container callback contract. In
src/inertia/src/InertiaServiceProvider.php lines 28-53, add native types to the
$app parameters of both singleton factory callbacks; in
tests/Inertia/Commands/StartSsrTest.php lines 30-35, type the factory callback
parameters used by bind(Process::class, ...). Preserve the existing binding
behavior.
- Around line 28-31: Update the singleton factory callback in
InertiaServiceProvider to type its $app parameter with the supported container
type, using Application if ServiceProvider::app provides that instance. Preserve
the existing Gateway::class binding and HttpGateway::class resolution.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: f9c3fea1-1905-4c74-961b-9212b8e24e61
📒 Files selected for processing (42)
docs/plans/2026-07-12-0900-framework-coroutine-state-lifecycle-audit.mddocs/plans/2026-07-12-0915-framework-coroutine-state-lifecycle-audit-ledger.mddocs/plans/2026-08-07-2018-inertia-correctness-ssr-lifecycle-and-current-parity.mdsrc/boost/docs/vite.mdsrc/inertia/README.mdsrc/inertia/config/inertia.phpsrc/inertia/src/Commands/StartSsr.phpsrc/inertia/src/Commands/StopSsr.phpsrc/inertia/src/Directive.phpsrc/inertia/src/ExceptionResponse.phpsrc/inertia/src/Inertia.phpsrc/inertia/src/InertiaServiceProvider.phpsrc/inertia/src/InertiaState.phpsrc/inertia/src/MergesProps.phpsrc/inertia/src/Middleware.phpsrc/inertia/src/Middleware/EnsureGetOnRedirect.phpsrc/inertia/src/PropsResolver.phpsrc/inertia/src/Response.phpsrc/inertia/src/ResponseFactory.phpsrc/inertia/src/ScrollProp.phpsrc/inertia/src/Ssr/HttpGateway.phpsrc/inertia/src/Ssr/SsrException.phpsrc/inertia/src/Ssr/SsrRenderFailed.phpsrc/inertia/src/View/Components/App.phpsrc/inertia/src/View/Components/Head.phpsrc/inertia/src/helpers.phptests/Inertia/BundleDetectorTest.phptests/Inertia/Commands/StartSsrTest.phptests/Inertia/Commands/StopSsrTest.phptests/Inertia/ComponentTest.phptests/Inertia/CoroutineIsolationTest.phptests/Inertia/DirectiveTest.phptests/Inertia/HelperTest.phptests/Inertia/HttpGatewayTest.phptests/Inertia/InertiaServiceProviderTest.phptests/Inertia/MergePropTest.phptests/Inertia/MiddlewareTest.phptests/Inertia/PropsResolverTest.phptests/Inertia/ResponseFactoryTest.phptests/Inertia/ResponseTest.phptests/Inertia/ScrollPropTest.phptests/Inertia/SsrRenderFailedTest.php
Document why the second malformed-response dispatch is load-bearing.\n\nThe assertion proves transport backoff is armed before throw_on_error raises the SSR exception, so the queued follow-up response must remain untouched.
|
@coderabbitai @greptile-apps I reviewed every main-thread, inline, nitpick, and pre-merge finding against the current Inertia server, Guzzle transport behavior, Hypervel's container conventions, and the Testbench runtime. One change was made: the malformed-response regression now explains that its second dispatch proves transport backoff was armed before The remaining suggestions do not require changes:
The focused regression and complete Inertia package suite are green. The follow-up is in |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
git show --stat --oneline --decorate --no-renames 7cb14c42d
git show --format=fuller --no-ext-diff --unified=80 7cb14c42d -- \
tests/Inertia/HttpGatewayTest.php \
src/inertia/src/Ssr/HttpGateway.php \
src/inertia/src/Commands/StopSsr.php \
src/inertia/src/Inertia.php \
src/inertia/src/InertiaServiceProvider.php
ast-grep outline tests/Inertia/HttpGatewayTest.php --items all --type function --match 'testMalformedSuccessDispatchesFailureAndHonorsThrowOnError|malformedSsrResponses'
ast-grep outline src/inertia/src/Commands/StopSsr.php --items all
ast-grep outline src/inertia/src/InertiaServiceProvider.php --items allLength of output: 8890
The second dispatch and the one-request assertion prove that malformed-response handling arms transport backoff before I accept the remaining decisions. The 🐇 ✓ ✏️ Learnings added
If you found this review helpful, would you consider giving us a shout-out on X? Thank you for using CodeRabbit! You are interacting with an AI system. |
|
The follow-up commit lands cleanly. The added comment in The reasoning on the declined suggestions holds:
The test suite additions look thorough: the data-provider coverage of malformed SSR shapes, the backoff semantics split across Good to merge. |
Summary
This PR completes the Inertia correctness and SSR lifecycle maintenance work for Hypervel's long-lived worker model.
The main changes are:
Vary, empty-response, falsey identifier, numeric prop-key, and initial-page JSON behavior;Request state
Provider boot may configure shared props, the root view, asset versions, SSR exclusions, and related defaults before request coroutines exist. That state now lives as one non-coroutine baseline. The first Inertia access inside a request shallow-clones the baseline into coroutine context.
This keeps boot configuration available while preventing one request's page, shared props, SSR result, or runtime configuration from leaking into another request. Mutable
ScrollPropinstances are cloned at the resolver boundary because their callback result and merge metadata are request-specific. Arbitrary user-owned prop objects are left alone.Protocol behavior
Middleware now applies
Vary: X-Inertiato the response that is actually returned and deduplicates the value case-insensitively. Version-mismatch location responses include the current asset version header. Only an exact empty response body triggers empty-response handling, so"0", streamed responses, and binary responses are preserved.Initial-page serialization now fails through the normal JSON exception path instead of silently emitting an empty payload. When SSR succeeds, the fallback page JSON is no longer encoded a second time. Root view data can no longer replace the framework-owned
pagevalue and make directives disagree with components.The remaining identifier and header boundaries use exact null and empty-string checks. This preserves valid
"0"values and numeric top-level prop keys without broad coercion changes elsewhere.SSR lifecycle
The gateway keeps one cookie-free Guzzle client so successful renders continue to reuse connections. Successful HTTP contact clears transport backoff. Connection failures and malformed transport responses arm it; structured page-render failures do not suppress unrelated pages.
Successful payloads are accepted only when they contain a string body and an array of string head entries. Remote error metadata is normalized before events are constructed, and remote error types are never treated as authority over worker state.
The development hot URL may be configured directly with
INERTIA_SSR_HOT_URL, or read from Vite's hot file. If that file disappears between publication checks, rendering falls back to the client without creating a synthetic SSR failure.inertia:start-ssrnow uses Hypervel's coroutine-aware signal handling and returns the child process result.inertia:stop-ssrperforms a bounded health check, requests shutdown through the concrete gateway, and distinguishes an unavailable server from a rejected shutdown. The response-less close used by the official SSR endpoint remains supported.Compatibility and performance
No supported Inertia method, named argument, prop type, middleware contract, helper, command option, runtime, or protected extension point is removed.
The existing performance design remains intact: immutable metadata stays worker-cached, SSR keeps a reusable connection, and the page finder reuses its successful lookup cache. Added request work is limited to bounded in-memory checks and the shallow copies required for isolation. Successful SSR removes the previous second full-page JSON encoding.
Current upstream DevTools support is intentionally left for the next Inertia work unit so it can be designed and reviewed independently.
Validation
composer fix, covering formatting, static analysis, the parallel component suite, Testbench package mode, and dogfood checks;For more details, see:
docs/plans/2026-08-07-2018-inertia-correctness-ssr-lifecycle-and-current-parity.mdSummary by CodeRabbit
New Features
Bug Fixes
Vary: X-Inertiaheaders.Documentation