{"version":3,"file":"ssrPrepass.mjs","names":[],"sources":["../src/ssrPrepass.ts"],"sourcesContent":["/**\n * Heavily based on urql's ssr\n * https://github.com/FormidableLabs/urql/blob/main/packages/next-urql/src/with-urql-client.ts\n */\nimport type { DehydratedState } from '@tanstack/react-query';\nimport { dehydrate } from '@tanstack/react-query';\nimport { createTRPCUntypedClient } from '@trpc/client';\nimport type { TRPCClientError, TRPCClientErrorLike } from '@trpc/client';\nimport type { CoercedTransformerParameters } from '@trpc/client/unstable-internals';\nimport { getTransformer } from '@trpc/client/unstable-internals';\nimport { getQueryClient } from '@trpc/react-query/shared';\nimport type {\n  AnyRouter,\n  Dict,\n  Maybe,\n} from '@trpc/server/unstable-core-do-not-import';\nimport type {\n  AppContextType,\n  NextPageContext,\n} from 'next/dist/shared/lib/utils';\nimport { createElement } from 'react';\nimport type { TRPCPrepassHelper, TRPCPrepassProps } from './withTRPC';\n\nfunction transformQueryOrMutationCacheErrors<\n  TState extends\n    DehydratedState['mutations'][0] | DehydratedState['queries'][0],\n>(result: TState): TState {\n  const error = result.state.error as Maybe<TRPCClientError<any>>;\n  if (error instanceof Error && error.name === 'TRPCClientError') {\n    const newError: TRPCClientErrorLike<any> = {\n      message: error.message,\n      data: error.data,\n      shape: error.shape,\n    };\n    return {\n      ...result,\n      state: {\n        ...result.state,\n        error: newError,\n      },\n    };\n  }\n  return result;\n}\n\nexport const ssrPrepass: TRPCPrepassHelper = (opts) => {\n  const { parent, WithTRPC, AppOrPage } = opts;\n  type $PrepassProps = TRPCPrepassProps<AnyRouter, any>;\n\n  const transformer = getTransformer(\n    (parent as CoercedTransformerParameters).transformer,\n  );\n  WithTRPC.getInitialProps = async (appOrPageCtx: AppContextType) => {\n    const shouldSsr = async () => {\n      if (typeof window !== 'undefined') {\n        return false;\n      }\n      if (typeof parent.ssr === 'function') {\n        try {\n          return await parent.ssr({ ctx: appOrPageCtx.ctx });\n        } catch {\n          return false;\n        }\n      }\n      return parent.ssr;\n    };\n    const ssrEnabled = await shouldSsr();\n    const AppTree = appOrPageCtx.AppTree;\n\n    // Determine if we are wrapping an App component or a Page component.\n    const isApp = !!appOrPageCtx.Component;\n    const ctx: NextPageContext = isApp\n      ? appOrPageCtx.ctx\n      : (appOrPageCtx as any as NextPageContext);\n\n    // Run the wrapped component's getInitialProps function.\n    let pageProps: Dict<unknown> = {};\n    if (AppOrPage.getInitialProps) {\n      const originalProps = await AppOrPage.getInitialProps(\n        appOrPageCtx as any,\n      );\n      const originalPageProps = isApp\n        ? (originalProps.pageProps ?? {})\n        : originalProps;\n\n      pageProps = {\n        ...originalPageProps,\n        ...pageProps,\n      };\n    }\n    const getAppTreeProps = (props: Record<string, unknown>) =>\n      isApp ? { pageProps: props } : props;\n\n    if (typeof window !== 'undefined' || !ssrEnabled) {\n      return getAppTreeProps(pageProps);\n    }\n\n    const config = parent.config({ ctx });\n    const trpcClient = createTRPCUntypedClient(config);\n    const queryClient = getQueryClient(config);\n\n    const trpcProp: $PrepassProps = {\n      config,\n      trpcClient,\n      queryClient,\n      ssrState: 'prepass',\n      ssrContext: ctx,\n    };\n    const prepassProps = {\n      pageProps,\n      trpc: trpcProp,\n    };\n\n    const reactDomServer = await import('react-dom/server');\n\n    // Run the prepass step on AppTree. This will run all trpc queries on the server.\n    // multiple prepass ensures that we can do batching on the server\n    while (true) {\n      // render full tree\n      reactDomServer.renderToString(createElement(AppTree, prepassProps));\n      if (!queryClient.isFetching()) {\n        // the render didn't cause the queryClient to fetch anything\n        break;\n      }\n\n      // wait until the query cache has settled it's promises\n      await new Promise<void>((resolve) => {\n        const unsub = queryClient.getQueryCache().subscribe((event) => {\n          if (event?.query.getObserversCount() === 0) {\n            resolve();\n            unsub();\n          }\n        });\n      });\n    }\n    const dehydratedCache = dehydrate(queryClient, {\n      shouldDehydrateQuery(query) {\n        // filter out queries that are marked as trpc: { ssr: false } or are not enabled, but make sure errors are dehydrated\n        const isExcludedFromSSr =\n          query.state.fetchStatus === 'idle' &&\n          query.state.status === 'pending';\n        return !isExcludedFromSSr;\n      },\n    });\n    // since error instances can't be serialized, let's make them into `TRPCClientErrorLike`-objects\n    const dehydratedCacheWithErrors = {\n      ...dehydratedCache,\n      queries: dehydratedCache.queries.map(transformQueryOrMutationCacheErrors),\n      mutations: dehydratedCache.mutations.map(\n        transformQueryOrMutationCacheErrors,\n      ),\n    };\n\n    // dehydrate query client's state and add it to the props\n    pageProps['trpcState'] = transformer.input.serialize(\n      dehydratedCacheWithErrors,\n    );\n\n    const appTreeProps = getAppTreeProps(pageProps);\n\n    const meta =\n      parent.responseMeta?.({\n        ctx,\n        clientErrors: [...dehydratedCache.queries, ...dehydratedCache.mutations]\n          .map((v) => v.state.error)\n          .flatMap((err) =>\n            err instanceof Error && err.name === 'TRPCClientError'\n              ? [err as TRPCClientError<AnyRouter>]\n              : [],\n          ),\n      }) ?? {};\n\n    for (const [key, value] of Object.entries(meta.headers ?? {})) {\n      if (typeof value === 'string') {\n        ctx.res?.setHeader(key, value);\n      }\n    }\n    if (meta.status && ctx.res) {\n      ctx.res.statusCode = meta.status;\n    }\n\n    return appTreeProps;\n  };\n};\n"],"mappings":";;;;;;;AAuBA,SAAS,oCAGP,QAAwB;CACxB,MAAM,QAAQ,OAAO,MAAM;CAC3B,IAAI,iBAAiB,SAAS,MAAM,SAAS,mBAAmB;EAC9D,MAAM,WAAqC;GACzC,SAAS,MAAM;GACf,MAAM,MAAM;GACZ,OAAO,MAAM;EACf;EACA,OAAA,eAAA,eAAA,CAAA,GACK,MAAA,GAAA,CAAA,GAAA,EACH,OAAA,eAAA,eAAA,CAAA,GACK,OAAO,KAAA,GAAA,CAAA,GAAA,EACV,OAAO,SAAA,CACT,EAAA,CACF;CACF;CACA,OAAO;AACT;AAEA,MAAa,cAAiC,SAAS;CACrD,MAAM,EAAE,QAAQ,UAAU,cAAc;CAGxC,MAAM,cAAc,eACjB,OAAwC,WAC3C;CACA,SAAS,kBAAkB,OAAO,iBAAiC;;EACjE,MAAM,YAAY,YAAY;GAC5B,IAAI,OAAO,WAAW,aACpB,OAAO;GAET,IAAI,OAAO,OAAO,QAAQ,YACxB,IAAI;IACF,OAAO,MAAM,OAAO,IAAI,EAAE,KAAK,aAAa,IAAI,CAAC;GACnD,SAAA,SAAQ;IACN,OAAO;GACT;GAEF,OAAO,OAAO;EAChB;EACA,MAAM,aAAa,MAAM,UAAU;EACnC,MAAM,UAAU,aAAa;EAG7B,MAAM,QAAQ,CAAC,CAAC,aAAa;EAC7B,MAAM,MAAuB,QACzB,aAAa,MACZ;EAGL,IAAI,YAA2B,CAAC;EAChC,IAAI,UAAU,iBAAiB;;GAC7B,MAAM,gBAAgB,MAAM,UAAU,gBACpC,YACF;GACA,MAAM,oBAAoB,SAAA,wBACrB,cAAc,eAAA,QAAA,0BAAA,KAAA,IAAA,wBAAa,CAAC,IAC7B;GAEJ,YAAA,eAAA,eAAA,CAAA,GACK,iBAAA,GACA,SACL;EACF;EACA,MAAM,mBAAmB,UACvB,QAAQ,EAAE,WAAW,MAAM,IAAI;EAEjC,IAAI,OAAO,WAAW,eAAe,CAAC,YACpC,OAAO,gBAAgB,SAAS;EAGlC,MAAM,SAAS,OAAO,OAAO,EAAE,IAAI,CAAC;EACpC,MAAM,aAAa,wBAAwB,MAAM;EACjD,MAAM,cAAc,eAAe,MAAM;EASzC,MAAM,eAAe;GACnB;GACA,MAAM;IARN;IACA;IACA;IACA,UAAU;IACV,YAAY;GAIC;EACf;EAEA,MAAM,iBAAiB,MAAM,OAAO;EAIpC,OAAO,MAAM;GAEX,eAAe,eAAe,cAAc,SAAS,YAAY,CAAC;GAClE,IAAI,CAAC,YAAY,WAAW,GAE1B;GAIF,MAAM,IAAI,SAAe,YAAY;IACnC,MAAM,QAAQ,YAAY,cAAc,CAAC,CAAC,WAAW,UAAU;KAC7D,KAAA,UAAA,QAAA,UAAA,KAAA,IAAA,KAAA,IAAI,MAAO,MAAM,kBAAkB,OAAM,GAAG;MAC1C,QAAQ;MACR,MAAM;KACR;IACF,CAAC;GACH,CAAC;EACH;EACA,MAAM,kBAAkB,UAAU,aAAa,EAC7C,qBAAqB,OAAO;GAK1B,OAAO,EAFL,MAAM,MAAM,gBAAgB,UAC5B,MAAM,MAAM,WAAW;EAE3B,EACF,CAAC;EAED,MAAM,4BAAA,eAAA,eAAA,CAAA,GACD,eAAA,GAAA,CAAA,GAAA;GACH,SAAS,gBAAgB,QAAQ,IAAI,mCAAmC;GACxE,WAAW,gBAAgB,UAAU,IACnC,mCACF;EACF,CAAA;EAGA,UAAU,eAAe,YAAY,MAAM,UACzC,yBACF;EAEA,MAAM,eAAe,gBAAgB,SAAS;EAE9C,MAAM,QAAA,wBAAA,wBACJ,OAAO,kBAAA,QAAA,0BAAA,KAAA,IAAA,KAAA,IAAA,sBAAA,KAAA,QAAe;GACpB;GACA,cAAc,CAAC,GAAG,gBAAgB,SAAS,GAAG,gBAAgB,SAAS,CAAC,CACrE,KAAK,MAAM,EAAE,MAAM,KAAK,CAAC,CACzB,SAAS,QACR,eAAe,SAAS,IAAI,SAAS,oBACjC,CAAC,GAAiC,IAClC,CAAC,CACP;EACJ,CAAC,OAAA,QAAA,yBAAA,KAAA,IAAA,uBAAK,CAAC;EAET,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,SAAA,gBAAQ,KAAK,aAAA,QAAA,kBAAA,KAAA,IAAA,gBAAW,CAAC,CAAC,GAC1D,IAAI,OAAO,UAAU,UAAU;;GAC7B,CAAA,WAAA,IAAI,SAAA,QAAA,aAAA,KAAA,KAAA,SAAK,UAAU,KAAK,KAAK;EAC/B;EAEF,IAAI,KAAK,UAAU,IAAI,KACrB,IAAI,IAAI,aAAa,KAAK;EAG5B,OAAO;CACT;AACF"}