Repository: vercel/next.js
Pull request: fix: rootParams should throw in client when fallbackParams are not present

`rootParams` was erroring if used in a `'prerender-client'`, but only if we had `fallbackRouteParams`. this is an edge case, but it is technically possible to hit if our compiler checks for using `rootParams` in client code are bypassed.

i've also had to split `PrerenderStoreModern` into two types instead of having `type: 'prerender' | 'prerender-client'`. otherwise, typescript would think that the `case 'prerender'` branch wasn't enough to eliminate `PrerenderStoreModern` even though we already handled the `prerender-client` case at the top. this made the `prerenderStore satisfies never` assertion at the bottom fail.

This is a bit tricky to explain, so i'm including a [TS playground repro](https://www.typescriptlang.org/play/?#code/C4TwDgpgBAysD2AnCAZCBzAhgYxFAvFAN5SiQBcUA5GMsgHYAmEiAtADYY4hUC+AUGWhwkEALLxmiegWKlwESjToQmLKlAA+1Wi1VTW2dgEtVwDQKGwEyWSORosuLddESp9fvwgAPMEmAoADMAV3psYGN4GQBnFgA3FgB5enYQABUAC2N6dAAKGJtFV2QASkpCxBz0Yn4oeqhjIKgCooA6K3wunRU1NiNTenNS2oax0kzEeAB3KHoIWYBRRCnEPKoAOXhAzHZ2GYhGRpkBsypSuoaBMZjp42BsTJbC0Q6FEaJL8agAeh+obCYOI9PR9QwmM5fcZ-KAAPSgAGFMPQqIFGPAJsYYlBMnooJgsDkADRQdIwKAsVbYu7AJ4AA3SCmoACJdAwDKchsyNFi5tsAfAALZgTCITAAI04pAxViorN6Bk4ThAzJc8tBUm5bTpULGMLy4oggJCwOm0EeRoA1ocJphAoagqIAIQXXUNQHA5Qa9SUT7fb7IYAhaRQZkAdQgVFsOXxUEFkhYMjZ+hYzLd9Wu-o90C97JYHC4uCovvT40DwZk4cj0ZkmCgSu4UGTfTT-qgme+zCCmBC7GAJbbNyKUBidqxQVM2PmiUQpb1-1hAH45w0YW11+KQoEyZj6JbsXSXsg6QDkSPInsoIbY-ZxAmQ7jbBBEjJafAQugnmaqIkKSZBTkdo2uK2xPP4MQxMYkrmkCEDYqWHbtvwAhAA)

i'm guessing that `type: 'prerender' | 'prerender-client'` is more complex than a normal discriminator and TS was getting confused, which is why having two separate types solves it.

Changed files:
filename: packages/next/src/server/app-render/work-unit-async-storage.external.ts
patch: @@ -78,12 +78,19 @@ export interface RequestStore extends CommonWorkUnitStore {
  * only needs to happen during the RSC prerender when we are prospectively prerendering
  * to fill all caches.
  */
-export interface PrerenderStoreModern extends CommonWorkUnitStore {
-  // In the future the prerender-client variant will get it's own type.
-  // prerender represents the RSC scope of the prerender.
-  // prerender-client represents the HTML scope of the prerender.
-  type: 'prerender' | 'prerender-client'
+export type PrerenderStoreModern =
+  | PrerenderStoreModernClient
+  | PrerenderStoreModernServer
 
+interface PrerenderStoreModernClient extends PrerenderStoreModernCommon {
+  type: 'prerender-client'
+}
+
+interface PrerenderStoreModernServer extends PrerenderStoreModernCommon {
+  type: 'prerender'
+}
+
+interface PrerenderStoreModernCommon extends CommonWorkUnitStore {
   /**
    * This signal is aborted when the React render is complete. (i.e. it is the same signal passed to react)
    */

filename: packages/next/src/server/request/root-params.ts
patch: @@ -66,6 +66,19 @@ function createPrerenderRootParams(
   workStore: WorkStore,
   prerenderStore: PrerenderStore
 ): Promise<Params> {
+  switch (prerenderStore.type) {
+    case 'prerender-client': {
+      const exportName = '`unstable_rootParams`'
+      throw new InvariantError(
+        `${exportName} must not be used within a client component. Next.js should be preventing ${exportName} from being included in client components statically, but did not in this case.`
+      )
+    }
+    case 'prerender':
+    case 'prerender-legacy':
+    case 'prerender-ppr':
+    default:
+  }
+
   const fallbackParams = workStore.fallbackRouteParams
   if (fallbackParams) {
     let hasSomeFallbackParams = false
@@ -93,11 +106,6 @@ function createPrerenderRootParams(
           CachedParams.set(underlyingParams, promise)
 
           return promise
-        case 'prerender-client':
-          const exportName = '`unstable_rootParams`'
-          throw new InvariantError(
-            `${exportName} must not be used within a client component. Next.js should be preventing ${exportName} from being included in client components statically, but did not in this case.`
-          )
         case 'prerender-ppr':
         case 'prerender-legacy':
           // We aren't in a dynamicIO prerender but we do have fallback params at this