feat(inertia): simple_module_inertia — a v3-capable Inertia adapter - #325
Conversation
…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
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Deploying simple-module-python with
|
| 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 |
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
|
Added one commit after opening: Found by running the e2e suite against the migrated v3 client (PR 2). Inertia 3's |
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
Summary
Replaces the
fastapi-inertiadependency 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 fromhxjo/fastapi-inertia(MIT, attribution inNOTICE) 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.mdWhy now
@inertiajs/react3.x is current and 2.x is taggedlegacy, but no v3-capable Python adapter exists —fastapi-inertia1.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:
_inertia_json.py— one encoder for both render branches (aPath/Decimal/UUIDprop no longer 500s on a client-side visit)_inertia_url.py— the pageurlis root-relative by construction (GH Host should honor X-Forwarded-Proto (proxy headers): Inertia pushState SecurityError behind a TLS-terminating proxy #223'spushStateSecurityErrorbehind a TLS proxy)_inertia_setup.py— the adapter reads the manifest by Vite's own keyBackwards 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
json.dumpsthenhtmlsafe_json_dumps); upstream only avoided that because its encoder returned a dict fromencode(). Fixed, and the render test asserts single encoding.views.py.tpl, which the plan had missed — every new module would otherwise have been generated importing the removed package.Verification
make lintgreen,make doctorcleandata-page; JSON branch returnsLandingat root-relative/witherrorspresent and no empty metadata fieldsOperator action before the next release tag
Register a PyPI pending publisher for
simple_module_inertia(ownerantosubash, reposimple_module_python, workflowrelease.yml). It is already in thepublish-pypimatrix; without the publisher that leg 403s and blocks the whole release.Out of scope
Precognition and SSR (separable;
templating.pykeeps upstream's SSR hooks but nothing calls them).https://claude.ai/code/session_01CwgTb8hULSfHoW2DrFrQAW