Skip to content

feat: inertia 3#23

Draft
NeoIsRecursive wants to merge 18 commits intomainfrom
feat/inertia-3
Draft

feat: inertia 3#23
NeoIsRecursive wants to merge 18 commits intomainfrom
feat/inertia-3

Conversation

@NeoIsRecursive
Copy link
Copy Markdown
Owner

@NeoIsRecursive NeoIsRecursive commented Mar 23, 2026

This PR implements everything that inertia v3 has:

Other things to explore before releasing this is #19 and see if we might be able to use discovery for sharing props and remove the share props from anywhere, personally ive never used it, but perhaps others do.

@coveralls
Copy link
Copy Markdown

coveralls commented Mar 23, 2026

Coverage Status

coverage: 92.754% (-6.1%) from 98.81%
when pulling 31eb93d on feat/inertia-3
into 46d3a3f on main.

@NeoIsRecursive NeoIsRecursive linked an issue Mar 30, 2026 that may be closed by this pull request
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR upgrades the Tempest Inertia adapter toward Inertia v3 by changing the HTML bootstrap format (JSON-in-<script type="application/json">), and introducing a dedicated prop-processing pipeline that supports v3 features like scroll props, merge props, and once props.

Changes:

  • Switch initial page bootstrap from a data-page="...json..." attribute to a <script type="application/json" data-page="...">... payload plus a plain root <div>.
  • Add a new PropPipeline (filter → evaluate → deferred/merge/scroll/once metadata) and wire it into InertiaResponse.
  • Implement v3 scroll props and once props (including related headers and page payload fields).

Reviewed changes

Copilot reviewed 32 out of 34 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tests/Integration/ViewTest.php Updates view rendering expectations for the new <script type="application/json"> bootstrap format.
tests/Integration/ScrollPropsTest.php Adds integration coverage for scroll props output (scrollProps, mergeProps).
tests/Integration/ResponseTest.php Updates server-rendered HTML expectations and adds extensive once-props behavioral tests.
tests/Integration/InertiaTest.php Adjusts JSON payload expectations to match updated PageData serialization.
tests/Integration/HistoryTest.php Updates tests around conditional history flags serialization.
src/Views/Components/x-inertia.view.php Implements the new v3 bootstrap template output (script tag + root div).
src/Support/Header.php Adds x-inertia-except-once-props header constant.
src/Props/ScrollProp.php Introduces ScrollProp to support scroll pagination payload + merge behavior.
src/Props/OptionalProp.php Refactors prop invocation via shared callable/once traits and marks it Onceable.
src/Props/DeferProp.php Refactors prop invocation via shared callable/once traits and marks it Onceable.
src/Props/AlwaysProp.php Refactors prop invocation via shared callable/once traits and marks it Onceable.
src/Pipeline/Stages/FilterProps.php Centralizes partial/always/once filtering into pipeline stage.
src/Pipeline/Stages/EvaluateProps.php Centralizes prop evaluation (closures/callable props/arrays/dot-unpacking) into pipeline stage.
src/Pipeline/Stages/ResolveDeferredProps.php Computes deferredProps, with special handling for loaded once-props.
src/Pipeline/Stages/ResolveMergeProps.php Computes mergeProps, including scroll-prop merge key behavior and reset support.
src/Pipeline/Stages/ResolveScrollProps.php Computes scrollProps metadata for scroll props.
src/Pipeline/Stages/ResolveOnceProps.php Computes onceProps metadata payload for onceable props.
src/Pipeline/PropStage.php Defines pipeline stage interface.
src/Pipeline/PropPipelineContext.php Introduces immutable-ish context container for staged prop processing.
src/Pipeline/PropPipeline.php Orchestrates the stages and returns ProcessedProps.
src/Pipeline/ProcessedProps.php DTO for processed props + metadata (deferredProps, mergeProps, etc.).
src/PageData.php Adds scrollProps/onceProps fields and omits history flags unless true.
src/Inertia.php Adds Inertia::once() helper and refactors request/version resolution approach.
src/Http/Middleware.php Adds redirect reflashing and fragment redirect conflict behavior (v3-related behavior).
src/Http/InertiaResponse.php Switches response prop processing to use the new PropPipeline.
src/Contracts/ProvidesScrollMetadata.php Adds contract for scroll metadata providers.
src/Contracts/Onceable.php Adds contract for once-prop behavior configuration and metadata.
src/Concerns/IsOnceProp.php Adds reusable implementation of Onceable configuration.
src/Concerns/IsMergeableProp.php Changes merge behavior to return a modified clone.
src/Concerns/IsCallableProp.php Adds shared callable-resolution helper for prop types.
mago.yaml Bumps configured PHP version for Mago analysis/linting.
composer.json Adds mago:analyze script.
composer.lock Updates dependency versions (Tempest + related tooling/libs).
.gitignore Ignores .vscode/settings.json.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +40 to +43
static::assertNotContains($body->toArray(), [
'encryptHistory',
'clearHistory',
]);
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

assertNotContains() arguments are reversed here: PHPUnit expects (needle, haystack), but this passes the entire $body->toArray() as the needle and a list of strings as the haystack, so it won’t actually assert that the keys are absent. Use assertArrayNotHasKey() for each key (or assert that array_keys($body->toArray()) doesn’t contain them).

Suggested change
static::assertNotContains($body->toArray(), [
'encryptHistory',
'clearHistory',
]);
static::assertArrayNotHasKey('encryptHistory', $body->toArray());
static::assertArrayNotHasKey('clearHistory', $body->toArray());

Copilot uses AI. Check for mistakes.
Comment thread src/Inertia.php
Comment on lines +58 to +61
public static function once(mixed $value): AlwaysProp
{
return new AlwaysProp($value)->once();
}
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

Inertia::once() returns an AlwaysProp, which makes once-props part of the “always” set in FilterProps and therefore they bypass X-Inertia-Partial-Data/X-Inertia-Partial-Except filtering on partial reloads. Once-props should still respect partial reload inclusion/exclusion (they’re only “once” in terms of resolution/caching), so consider introducing a dedicated OnceProp type (not AlwaysProp) or adjusting FilterProps so once-props aren’t treated as always-props.

Copilot uses AI. Check for mistakes.
Comment thread src/Props/ScrollProp.php
Comment on lines +41 to +50
protected function resolveValue(): mixed
{
if ($this->resolved !== null) {
return $this->resolved;
}

$this->resolved = $this->resolveCallablePropValue($this->value);

return $this->resolved;
}
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

resolveValue() treats null as “not resolved yet”, so a callable that legitimately resolves to null will be invoked repeatedly. Consider tracking resolution with a separate boolean/sentinel so null results are cached too.

Copilot uses AI. Check for mistakes.
Comment thread src/Props/ScrollProp.php
Comment on lines +130 to +134
if ($value instanceof PaginatedData) {
return [
$this->wrapper => $value->data,
];
}
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

When wrapper is set to an empty string, mergeKey() already avoids dot-wrapping, but __invoke() will still return an array with an empty-string key (['' => $value->data]). If empty wrapper is meant to mean “no wrapper”, __invoke() should return $value->data directly in that case.

Copilot uses AI. Check for mistakes.
Comment thread src/Http/Middleware.php
Comment on lines +81 to +84
private function prefetch(Request $request)
{
return (
strcasecmp($request->headers->get('HTTP_X_MOZ') ?? '', 'prefetch') === 0
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

The prefetch detection checks header name HTTP_X_MOZ, which looks like a server/env var rather than an actual HTTP header; with Request->headers->get() this will likely never match real requests. Use the actual header name (e.g. X-Moz) and consider adding a : bool return type for prefetch() to make intent explicit.

Suggested change
private function prefetch(Request $request)
{
return (
strcasecmp($request->headers->get('HTTP_X_MOZ') ?? '', 'prefetch') === 0
private function prefetch(Request $request): bool
{
return (
strcasecmp($request->headers->get('X-Moz') ?? '', 'prefetch') === 0

Copilot uses AI. Check for mistakes.
Comment on lines +21 to +22
public function test_scroll_props_renders_correctly()
{
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

For consistency with the rest of the integration tests in this repo, this test method should declare a : void return type.

Copilot uses AI. Check for mistakes.
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.

Inertia 3.0

3 participants