Skip to content

feat(inertia): simple_module_inertia — a v3-capable Inertia adapter - #325

Merged
antosubash merged 17 commits into
mainfrom
feat/inertia-v3-adapter
Sep 11, 2026
Merged

antosubash merged 17 commits into
mainfrom
feat/inertia-v3-adapter

Conversation

@antosubash

Copy link
Copy Markdown
Owner

Summary

Replaces the fastapi-inertia dependency with a sixth framework package, simple_module_inertia, that implements the full Inertia v3 page-object protocol — every prop type (optional, always, defer, merge, prepend, deep_merge, once, scroll), every partial-reload header (Partial-Data, Partial-Except, Reset, Except-Once-Props, Infinite-Scroll-Merge-Intent), history flags, and fragment-preserving redirects. Forked from hxjo/fastapi-inertia (MIT, attribution in NOTICE) and split by responsibility so every stage is a pure, unit-tested function.

Design: docs/superpowers/specs/2026-09-10-inertia-v3-adapter-design.md · Plan: docs/superpowers/plans/2026-09-10-inertia-v3-adapter.md

Why now

@inertiajs/react 3.x is current and 2.x is tagged legacy, but no v3-capable Python adapter exists — fastapi-inertia 1.1.0 implements only the v2 subset (flash, deferred). This is the server half that unblocks the client upgrade (PR 2 follows).

What hosting sheds

Three workarounds that wrapped upstream are deleted because their fixes now live in the adapter:

Backwards compatibility

The four required page fields are unchanged and every v3 addition is conditional, so a v2 client keeps working. The full suite (3024 tests) passes against the existing v2 client; the module-facing contract (InertiaDep, InertiaResponse, render, share) kept its names, so the 14 call sites changed one import line each.

Found during execution

  • The plan's HTML branch would have double-encoded the page object (json.dumps then htmlsafe_json_dumps); upstream only avoided that because its encoder returned a dict from encode(). Fixed, and the render test asserts single encoding.
  • The import sweep caught the module scaffold's views.py.tpl, which the plan had missed — every new module would otherwise have been generated importing the removed package.

Verification

  • 71 adapter tests (17 protocol-conformance rows for the resolution engine), full suite green, make lint green, make doctor clean
  • Live boot: HTML branch 200 with a single-encoded data-page; JSON branch returns Landing at root-relative / with errors present and no empty metadata fields

Operator action before the next release tag

Register a PyPI pending publisher for simple_module_inertia (owner antosubash, repo simple_module_python, workflow release.yml). It is already in the publish-pypi matrix; without the publisher that leg 403s and blocks the whole release.

Out of scope

Precognition and SSR (separable; templating.py keeps upstream's SSR hooks but nothing calls them).

https://claude.ai/code/session_01CwgTb8hULSfHoW2DrFrQAW

…pter

Fork fastapi-inertia (MIT) into a sixth framework package that implements
the full v3 page-object protocol — every prop type, every partial-reload
header, history flags, fragment-preserving redirects — so the client can
leave @inertiajs/react 2.x. Absorbs hosting's three upstream workarounds.
Precognition and SSR are out of scope.

Claude-Session: https://claude.ai/code/session_01CwgTb8hULSfHoW2DrFrQAW
… migration

Sixteen TDD tasks: package skeleton through public surface (1-10), the
hosting and call-site switchover proven against the v2 client (11-12),
docs and PR 1 (13), then the client bump, the three v3 code changes and
browser proof as PR 2 (14-16).

Claude-Session: https://claude.ai/code/session_01CwgTb8hULSfHoW2DrFrQAW
Sixth framework package, lockstep-versioned, wired into pytest testpaths
and the PyPI release matrix. Empty for now; the adapter lands in the
following commits.

Claude-Session: https://claude.ai/code/session_01CwgTb8hULSfHoW2DrFrQAW
Full-visit vs partial-reload, Partial-Except, Reset, once-except,
merge/prepend/deep with matchPropsOn, deferred groups, scroll cursors,
shared-under-page precedence — each covered by a conformance test.

Claude-Session: https://claude.ai/code/session_01CwgTb8hULSfHoW2DrFrQAW
Absorbs hosting's _inertia_url and _inertia_json wraps: the url is
root-relative by construction and both render branches share one
jsonable_encoder pass that names the failing prop path on error.

Claude-Session: https://claude.ai/code/session_01CwgTb8hULSfHoW2DrFrQAW
The facade is thin — every stage lives in its own module. One fix over
the plan as written: the HTML branch passes encode_page()'s dict straight
to htmlsafe_json_dumps; dumping to a string first double-encoded the
page object (upstream only avoided that because its encoder returned a
dict from encode()). The render test asserts single encoding.

Claude-Session: https://claude.ai/code/session_01CwgTb8hULSfHoW2DrFrQAW
…-inertia

Deletes the three upstream workarounds — the JSON-encoder wrap, the
relative-url wrap and the Vite manifest re-keying — whose fixes now
live in the adapter itself. The package README gains the Install and
Usage sections the README gate requires.

Claude-Session: https://claude.ai/code/session_01CwgTb8hULSfHoW2DrFrQAW
One-line import change per view module, the host routes, and both
scaffold templates; render() and the dependency are unchanged. The
full suite passing against the v2 client is the proof the adapter is
backwards compatible.

The sweep also caught the module scaffold's views.py.tpl, which the
plan had missed — without it every new module would have been
generated importing the removed package.

Claude-Session: https://claude.ai/code/session_01CwgTb8hULSfHoW2DrFrQAW
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
🔒 Security Review Completed 2026-09-10T20:26:39.336773Z 31a2b30 PR opened
ℹ️ About Codex in GitHub

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

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

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 10, 2026

Copy link
Copy Markdown

Deploying simple-module-python with  Cloudflare Pages  Cloudflare Pages

Latest commit: 8759eb0
Status: ✅  Deploy successful!
Preview URL: https://84132f7d.simple-module-python.pages.dev
Branch Preview URL: https://feat-inertia-v3-adapter.simple-module-python.pages.dev

View logs

Inertia 3's getInitialPageFromDOM reads only
<script data-page="app" type="application/json">; the data-page
attribute path Inertia 2 used is gone from the 3.x bundle entirely, so
the v3 client found no page and threw on page.component. The body now
emits both forms from the one htmlsafe_json_dumps string — a v2 client
keeps working, a v3 client mounts. Caught by e2e against the migrated
client; the spec's templating row now records the protocol fact.

Claude-Session: https://claude.ai/code/session_01CwgTb8hULSfHoW2DrFrQAW
@antosubash

Copy link
Copy Markdown
Owner Author

Added one commit after opening: d0936ec8 fix(inertia): deliver the initial page the way Inertia 3 reads it.

Found by running the e2e suite against the migrated v3 client (PR 2). Inertia 3's getInitialPageFromDOM reads only <script data-page="app" type="application/json">; the data-page attribute on <div id="app"> that Inertia 2 used is gone from the 3.x bundle entirely, so the v3 client found no page and threw Cannot read properties of null (reading 'component'). The adapter now emits both forms from the one htmlsafe_json_dumps string — a v2 client keeps working, a v3 client mounts. Covered by test_adapter_templating.py; the spec's templating.py row records the protocol fact.

@antosubash
antosubash added this pull request to stack #327 September 10, 2026 20:55
The dual-form initial page added a second data-page occurrence, and this
helper searched the double-quoted form first — which now matches the
script element's literal data-page="app" attribute naming the mount
point, not JSON. It parsed "app" and raised JSONDecodeError, failing
all 13 error-page tests in CI.

The helper now reads the JSON script element first, exactly as the v3
client does, keeping the attribute form as the v2 fallback. Every other
data-page reference was checked: the e2e helper reads
getElementById('app').dataset.page (the form that was kept) and the rest
are substring assertions.

Claude-Session: https://claude.ai/code/session_01CwgTb8hULSfHoW2DrFrQAW
@antosubash
antosubash merged commit b7844a8 into main Sep 11, 2026
13 checks passed
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.

1 participant