Skip to content

fix(prerender): emit /app/checkout + /app/billing SPA shells (HTTP 200, was 404)#144

Merged
mastermanas805 merged 2 commits into
mainfrom
fix/spa-fallback-200
May 29, 2026
Merged

fix(prerender): emit /app/checkout + /app/billing SPA shells (HTTP 200, was 404)#144
mastermanas805 merged 2 commits into
mainfrom
fix/spa-fallback-200

Conversation

@mastermanas805
Copy link
Copy Markdown
Member

Summary

UI-3 from the QA backlog (P2). External CTA entries to /app/checkout (from /pricing → "Start Pro") and /app/billing (from magic-link → "manage billing") shipped HTTP 404 status. Body hydrated correctly via GH Pages' 404.html SPA fallback, but the status code lied — analytics noise + tab-strip title flash + SEO confusion if either URL were ever indexed.

Root cause

scripts/prerender.mjs Step 4.6 emits pre-generated SPA shells for /login, /login/callback, /claim, /cli-auth, but no /app/* sub-paths. /app itself gets a shell (Step 4.5), but bookmarked /app/checkout and /app/billing direct hits never get HTTP 200.

Fix

Add /app/checkout and /app/billing to Step 4.6's authShellRoutes. GH Pages serves dist/app/checkout/index.html and dist/app/billing/index.html with HTTP 200; the SPA hydrates as before.

Other /app/* deep links (/app/resources/<id>, etc.) stay on the 404 fallback — they aren't external CTA destinations and aren't worth pre-generating with static paths.

Also adds ROUTE_META entries for both routes so the pre-rendered <title> shows Checkout · instanode / Billing · instanode instead of bleeding through the homepage title.

Gate verification

npm run gate   # 1043 passed | 3 skipped
ls -la dist/app/checkout/index.html dist/app/billing/index.html
# -rw-r--r--@ 1 ... 5287 ... dist/app/checkout/index.html
# -rw-r--r--@ 1 ... 5366 ... dist/app/billing/index.html
grep -c "Checkout · instanode" dist/app/checkout/index.html   # 3
grep -c "Billing · instanode"  dist/app/billing/index.html    # 3

Live verify (after merge)

curl -sI https://instanode.dev/app/checkout    # expect HTTP/2 200 (was 404)
curl -sI https://instanode.dev/app/billing     # expect HTTP/2 200 (was 404)
curl -s  https://instanode.dev/app/billing | grep -c "Billing"   # > 0

Regression coverage

src/prerender.test.ts (new) — static check on scripts/prerender.mjs:

  • both new routes appear in the authShellRoutes literal
  • the original 4 auth routes still present
  • ROUTE_META has titles for both new routes

🤖 Generated with Claude Code

…0, was 404)

UI-3 from the QA backlog (P2). External CTA entries to /app/checkout
(from /pricing → "Start Pro") and /app/billing (from magic-link → "manage
billing") shipped HTTP 404 status. Body hydrated correctly via GH Pages'
404.html SPA fallback (catch-all React Route picks up the path on
hydrate), but the status code lied — analytics noise + tab-strip title
flash + SEO confusion if the URLs were ever indexed.

Root cause: scripts/prerender.mjs Step 4.6 emits pre-generated SPA
shells for /login, /login/callback, /claim, /cli-auth, but no /app/*
sub-paths. /app itself gets a shell (Step 4.5), but bookmarked /app/checkout
and /app/billing direct hits never get HTTP 200.

Fix: add /app/checkout and /app/billing to Step 4.6's authShellRoutes.
GH Pages now serves dist/app/checkout/index.html and
dist/app/billing/index.html with HTTP 200; the SPA hydrates as before.
Other /app/* deep links (/app/resources/<id>, etc.) stay on the 404
fallback — they aren't external CTA destinations and aren't worth
pre-generating with static paths.

Also add ROUTE_META entries for both routes so the pre-rendered <title>
shows "Checkout · instanode" / "Billing · instanode" instead of bleeding
through the homepage title.

Verified via gate (npm run build):
  dist/app/checkout/index.html → exists, <title>Checkout · instanode</title>
  dist/app/billing/index.html  → exists, <title>Billing · instanode</title>

Regression coverage in src/prerender.test.ts:
  - both new routes appear in the authShellRoutes literal
  - original 4 auth routes still present
  - ROUTE_META has titles for both new routes

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 29, 2026

size-limit report 📦

Path Size
dist/assets/index-Ex6CmsJ1.js 163.55 KB (0%)
dist/assets/index-BsJUZYRr.css 6.13 KB (0%)

@mastermanas805 mastermanas805 merged commit 6e330f2 into main May 29, 2026
16 of 17 checks passed
@mastermanas805 mastermanas805 deleted the fix/spa-fallback-200 branch May 29, 2026 08:48
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