* refactor(pages): extract render orchestrator from codegen string (#1784)
Move `_renderPage` (~260 lines) and `collectAssetTags` (~85 lines) out
of the generated template string in `entries/pages-server-entry.ts` into
typed, unit-testable server modules:
- `server/pages-page-handler.ts` — `createPagesPageHandler` factory that
owns the full Pages Router render lifecycle (i18n, route matching,
404/500 fallback recursion, _next/data envelope, 405 method check,
ISR cache-key derivation, SSR-context setup). Mirrors how
`server/app-rsc-handler.ts` is structured relative to
`entries/app-rsc-entry.ts`.
- `server/pages-asset-tags.ts` — `collectAssetTags` + manifest helpers,
extracted from the template so they can be unit-tested directly.
- `server/pages-data-route.ts` — adds `normalizePagesDataRequest` so both
`renderPage` and `runMiddleware` share one implementation instead of
duplicating the buildId-check logic in the template string.
`entries/pages-server-entry.ts` is now thin wiring: route-table codegen,
embedded config/i18n/buildId, and closures that delegate to the handler.
`renderPage` keeps its exact exported signature; all callers are unaffected.
No behavior change — pure code motion. Fixed a latent bug where the
`isrCacheKey` wrapper in the handler was missing the `buildId` argument on
non-i18n paths (causing a cache-key mismatch); caught by the existing
ISR CSP-nonce test.
* refactor(pages): tighten handler types and improve tests
- Use ComponentType from react for AppComponent, DocumentComponent, and
PageComponent closures instead of unknown — matches pages-page-response.ts
which already uses ComponentType. Eliminates the DocumentComponent cast.
- Widen isrCacheKey router param from literal union to string — the literal
union forced as-casts everywhere it was used indirectly. All call sites
pass 'pages' or 'app' string literals so the widening is safe.
- Replace the shallow i18n redirect test (which just checked a route miss
gave 404) with a test that actually exercises the 307 short-circuit:
visiting / with Accept-Language: fr triggers resolvePagesI18nRequest to
return redirectUrl and the handler returns 307 to /fr.
- Add second i18n test: locale prefix already present → no redirect.
- Tighten 'allows POST with gSSP' test: assert no Allow header (confirms
the 405 guard was bypassed, not just that some non-405 came back).