Split out of #1849, which is otherwise resolved. This is the one residual: defineMiddleware(handler, { mode: 'client' }) is still never executed in the browser.
Why it is not just wiring
The SPA router (packages/router/src/client.ts) is a stringified runtime — getRouterScript() returns a template literal that is injected as a <script>. It cannot import { runMiddleware }. The middleware registry (packages/router/src/middleware.ts) is a server-side Map populated when middleware definitions evaluate in Node (SSG / dev server). Neither the registry nor runMiddleware is exposed to the browser (grep -rn 'getMiddlewareNames\|runMiddleware\|middlewareRegistry' client.ts signals.ts → nothing).
So executing client-mode middleware needs new infrastructure, not a call site:
- Ship
mode: 'client'middleware definitions to the browser (bundle them; the definitions currently only run server-side). - Expose a client-side registry +
runMiddlewareto the router script (e.g. onwindow.stx), populated by those definitions. - Call it from the SPA navigation path in the router script, honoring
redirect/abort— mirroring the existing server path indev-server/serve-app.ts:705and the redirect-honoring already added infe50af9036.
Scope note: run only mode: 'client' here. universal middleware already runs server-side on the SPA fragment fetch, so running it client-side too would double-execute side effects.
Why it can wait
The practical need #1849 was filed for — redirect unauthenticated users on SPA navigation — is already met by server/universal middleware (serve-app.ts runs it, client.ts honors the 302). Pure client-mode is only needed for a guard that must decide in the browser with no round-trip (e.g. off client-only state). Lower value, higher cost; hence its own issue.