{"version":3,"sources":["../../src/hydration.ts"],"sourcesContent":["import { tryResolveSync } from './thenable'\nimport { noop } from './utils'\nimport type {\n  DefaultError,\n  MutationKey,\n  MutationMeta,\n  MutationOptions,\n  MutationScope,\n  QueryKey,\n  QueryMeta,\n  QueryOptions,\n} from './types'\nimport type { QueryClient } from './queryClient'\nimport type { Query, QueryState } from './query'\nimport type { Mutation, MutationState } from './mutation'\n\n// TYPES\ntype TransformerFn = (data: any) => any\nfunction defaultTransformerFn(data: any): any {\n  return data\n}\n\nexport interface DehydrateOptions {\n  serializeData?: TransformerFn\n  shouldDehydrateMutation?: (mutation: Mutation) => boolean\n  shouldDehydrateQuery?: (query: Query) => boolean\n  shouldRedactErrors?: (error: unknown) => boolean\n}\n\nexport interface HydrateOptions {\n  defaultOptions?: {\n    deserializeData?: TransformerFn\n    queries?: QueryOptions\n    mutations?: MutationOptions<unknown, DefaultError, unknown, unknown>\n  }\n}\n\ninterface DehydratedMutation {\n  mutationKey?: MutationKey\n  state: MutationState\n  meta?: MutationMeta\n  scope?: MutationScope\n}\n\ninterface DehydratedQuery {\n  queryHash: string\n  queryKey: QueryKey\n  state: QueryState\n  promise?: Promise<unknown>\n  meta?: QueryMeta\n  // This is only optional because older versions of Query might have dehydrated\n  // without it which we need to handle for backwards compatibility.\n  // This should be changed to required in the future.\n  dehydratedAt?: number\n}\n\nexport interface DehydratedState {\n  mutations: Array<DehydratedMutation>\n  queries: Array<DehydratedQuery>\n}\n\n// FUNCTIONS\n\nfunction dehydrateMutation(mutation: Mutation): DehydratedMutation {\n  return {\n    mutationKey: mutation.options.mutationKey,\n    state: mutation.state,\n    ...(mutation.options.scope && { scope: mutation.options.scope }),\n    ...(mutation.meta && { meta: mutation.meta }),\n  }\n}\n\n// Most config is not dehydrated but instead meant to configure again when\n// consuming the de/rehydrated data, typically with useQuery on the client.\n// Sometimes it might make sense to prefetch data on the server and include\n// in the html-payload, but not consume it on the initial render.\nfunction dehydrateQuery(\n  query: Query,\n  serializeData: TransformerFn,\n  shouldRedactErrors: (error: unknown) => boolean,\n): DehydratedQuery {\n  const dehydratePromise = () => {\n    const promise = query.promise?.then(serializeData).catch((error) => {\n      if (!shouldRedactErrors(error)) {\n        // Reject original error if it should not be redacted\n        return Promise.reject(error)\n      }\n      // If not in production, log original error before rejecting redacted error\n      if (process.env.NODE_ENV !== 'production') {\n        console.error(\n          `A query that was dehydrated as pending ended up rejecting. [${query.queryHash}]: ${error}; The error will be redacted in production builds`,\n        )\n      }\n      return Promise.reject(new Error('redacted'))\n    })\n\n    // Avoid unhandled promise rejections\n    // We need the promise we dehydrate to reject to get the correct result into\n    // the query cache, but we also want to avoid unhandled promise rejections\n    // in whatever environment the prefetches are happening in.\n    promise?.catch(noop)\n\n    return promise\n  }\n\n  return {\n    dehydratedAt: Date.now(),\n    state: {\n      ...query.state,\n      ...(query.state.data !== undefined && {\n        data: serializeData(query.state.data),\n      }),\n    },\n    queryKey: query.queryKey,\n    queryHash: query.queryHash,\n    ...(query.state.status === 'pending' && {\n      promise: dehydratePromise(),\n    }),\n    ...(query.meta && { meta: query.meta }),\n  }\n}\n\nexport function defaultShouldDehydrateMutation(mutation: Mutation) {\n  return mutation.state.isPaused\n}\n\nexport function defaultShouldDehydrateQuery(query: Query) {\n  return query.state.status === 'success'\n}\n\nfunction defaultShouldRedactErrors(_: unknown) {\n  return true\n}\n\nexport function dehydrate(\n  client: QueryClient,\n  options: DehydrateOptions = {},\n): DehydratedState {\n  const filterMutation =\n    options.shouldDehydrateMutation ??\n    client.getDefaultOptions().dehydrate?.shouldDehydrateMutation ??\n    defaultShouldDehydrateMutation\n\n  const mutations = client\n    .getMutationCache()\n    .getAll()\n    .flatMap((mutation) =>\n      filterMutation(mutation) ? [dehydrateMutation(mutation)] : [],\n    )\n\n  const filterQuery =\n    options.shouldDehydrateQuery ??\n    client.getDefaultOptions().dehydrate?.shouldDehydrateQuery ??\n    defaultShouldDehydrateQuery\n\n  const shouldRedactErrors =\n    options.shouldRedactErrors ??\n    client.getDefaultOptions().dehydrate?.shouldRedactErrors ??\n    defaultShouldRedactErrors\n\n  const serializeData =\n    options.serializeData ??\n    client.getDefaultOptions().dehydrate?.serializeData ??\n    defaultTransformerFn\n\n  const queries = client\n    .getQueryCache()\n    .getAll()\n    .flatMap((query) =>\n      filterQuery(query)\n        ? [dehydrateQuery(query, serializeData, shouldRedactErrors)]\n        : [],\n    )\n\n  return { mutations, queries }\n}\n\nexport function hydrate(\n  client: QueryClient,\n  dehydratedState: unknown,\n  options?: HydrateOptions,\n): void {\n  if (typeof dehydratedState !== 'object' || dehydratedState === null) {\n    return\n  }\n\n  const mutationCache = client.getMutationCache()\n  const queryCache = client.getQueryCache()\n  const deserializeData =\n    options?.defaultOptions?.deserializeData ??\n    client.getDefaultOptions().hydrate?.deserializeData ??\n    defaultTransformerFn\n\n  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n  const mutations = (dehydratedState as DehydratedState).mutations || []\n  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n  const queries = (dehydratedState as DehydratedState).queries || []\n\n  mutations.forEach(({ state, ...mutationOptions }) => {\n    mutationCache.build(\n      client,\n      {\n        ...client.getDefaultOptions().hydrate?.mutations,\n        ...options?.defaultOptions?.mutations,\n        ...mutationOptions,\n      },\n      state,\n    )\n  })\n\n  queries.forEach(\n    ({ queryKey, state, queryHash, meta, promise, dehydratedAt }) => {\n      const syncData = promise ? tryResolveSync(promise) : undefined\n      const rawData = state.data === undefined ? syncData?.data : state.data\n      const data = rawData === undefined ? rawData : deserializeData(rawData)\n\n      let query = queryCache.get(queryHash)\n      const existingQueryIsPending = query?.state.status === 'pending'\n      const existingQueryIsFetching = query?.state.fetchStatus === 'fetching'\n\n      // Do not hydrate if an existing query exists with newer data\n      if (query) {\n        const hasNewerSyncData =\n          syncData &&\n          // We only need this undefined check to handle older dehydration\n          // payloads that might not have dehydratedAt\n          dehydratedAt !== undefined &&\n          dehydratedAt > query.state.dataUpdatedAt\n        if (\n          state.dataUpdatedAt > query.state.dataUpdatedAt ||\n          hasNewerSyncData\n        ) {\n          // omit fetchStatus from dehydrated state\n          // so that query stays in its current fetchStatus\n          const { fetchStatus: _ignored, ...serializedState } = state\n          query.setState({\n            ...serializedState,\n            data,\n          })\n        }\n      } else {\n        // Restore query\n        query = queryCache.build(\n          client,\n          {\n            ...client.getDefaultOptions().hydrate?.queries,\n            ...options?.defaultOptions?.queries,\n            queryKey,\n            queryHash,\n            meta,\n          },\n          // Reset fetch status to idle to avoid\n          // query being stuck in fetching state upon hydration\n          {\n            ...state,\n            data,\n            fetchStatus: 'idle',\n            status: data !== undefined ? 'success' : state.status,\n          },\n        )\n      }\n\n      if (\n        promise &&\n        !existingQueryIsPending &&\n        !existingQueryIsFetching &&\n        // Only hydrate if dehydration is newer than any existing data,\n        // this is always true for new queries\n        (dehydratedAt === undefined || dehydratedAt > query.state.dataUpdatedAt)\n      ) {\n        // This doesn't actually fetch - it just creates a retryer\n        // which will re-use the passed `initialPromise`\n        // Note that we need to call these even when data was synchronously\n        // available, as we still need to set up the retryer\n        query\n          .fetch(undefined, {\n            // RSC transformed promises are not thenable\n            initialPromise: Promise.resolve(promise).then(deserializeData),\n          })\n          // Avoid unhandled promise rejections\n          .catch(noop)\n      }\n    },\n  )\n}\n"],"mappings":"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","names":[]}