{"version":3,"file":"hydration.cjs","names":["noop"],"sources":["../../src/hydration.ts"],"sourcesContent":["import { 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\n\nfunction tryResolveSync(promise: PromiseLike<unknown>) {\n  let data: unknown\n\n  const thenResult = promise.then((result) => {\n    data = result\n    return result\n  }, noop) as Promise<unknown> | undefined\n\n  // .catch can be unavailable on certain kinds of thenable's\n  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n  thenResult?.catch?.(noop)\n\n  if (data !== undefined) {\n    return { data }\n  }\n\n  return undefined\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  queryType?: 'infinite'\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\nfunction dehydratePromise(\n  query: Query,\n  serializeData?: TransformerFn,\n  shouldRedactErrors?: (error: unknown) => boolean,\n): Promise<unknown> | undefined {\n  const promise = query.promise?.then(serializeData).catch((error) => {\n    if (shouldRedactErrors?.(error) === false) {\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// 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.\nexport function dehydrateQuery(\n  query: Query,\n  serializeData?: TransformerFn,\n  shouldRedactErrors?: (error: unknown) => boolean,\n): DehydratedQuery {\n  return {\n    dehydratedAt: Date.now(),\n    state: {\n      ...query.state,\n      ...(query.state.data !== undefined && {\n        data: serializeData\n          ? serializeData(query.state.data)\n          : query.state.data,\n      }),\n    },\n    queryKey: query.queryKey,\n    queryHash: query.queryHash,\n    ...(query.state.status === 'pending' && {\n      promise: dehydratePromise(query, serializeData, shouldRedactErrors),\n    }),\n    ...(query.meta && { meta: query.meta }),\n    ...(query.queryType && { queryType: query.queryType }),\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\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\n  const serializeData =\n    options.serializeData ?? client.getDefaultOptions().dehydrate?.serializeData\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: Partial<DehydratedState>,\n  options?: HydrateOptions,\n): void {\n  const mutationCache = client.getMutationCache()\n  const queryCache = client.getQueryCache()\n  const deserializeData =\n    options?.defaultOptions?.deserializeData ??\n    client.getDefaultOptions().hydrate?.deserializeData\n\n  dehydratedState.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  dehydratedState.queries?.forEach(\n    ({\n      queryKey,\n      state,\n      queryHash,\n      meta,\n      promise,\n      dehydratedAt,\n      queryType,\n    }) => {\n      const syncData = promise ? tryResolveSync(promise) : undefined\n      const rawData = state.data === undefined ? syncData?.data : state.data\n      const data =\n        rawData === undefined\n          ? rawData\n          : deserializeData\n            ? deserializeData(rawData)\n            : 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 so that query stays in its current fetchStatus\n          const { fetchStatus: _ignored, ...serializedState } = state\n          query.setState({\n            ...serializedState,\n            data,\n            // If the query was pending at the moment of dehydration, but resolved to have data\n            // before hydration, we can assume the query should be hydrated as successful.\n            //\n            // Since you can opt into dehydrating failed queries, and those can have data from\n            // previous successful fetches, we make sure we only do this for pending queries.\n            ...(state.status === 'pending' &&\n              data !== undefined && {\n                status: 'success' as const,\n                dataUpdatedAt: dehydratedAt ?? Date.now(),\n                // Preserve existing fetchStatus if the existing query is actively fetching.\n                ...(!existingQueryIsFetching && {\n                  fetchStatus: 'idle' as const,\n                }),\n              }),\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            _type: queryType,\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            // Like above, if the query was pending at the moment of dehydration but has data,\n            // we can assume it should be hydrated as successful.\n            status:\n              state.status === 'pending' && data !== undefined\n                ? 'success'\n                : state.status,\n            ...(state.status === 'pending' &&\n              data !== undefined && {\n                dataUpdatedAt: dehydratedAt ?? Date.now(),\n              }),\n          },\n        )\n      }\n\n      if (\n        promise &&\n        // If the data was synchronously available, there is no need to set up\n        // a retryer and thus no reason to call fetch\n        !syncData &&\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        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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"}