import { MutationObserver, shouldThrowError, QueriesObserver, noop, QueryClient as QueryClient$1, replaceEqualDeep, hydrate, notifyManager, QueryObserver, InfiniteQueryObserver } from '@tanstack/query-core';
export * from '@tanstack/query-core';
import { createContext, useContext, createRenderEffect, onCleanup, createMemo, createComputed, on, mergeProps, createResource, batch, onMount, createSignal, createEffect } from 'solid-js';
import { createComponent, isServer } from 'solid-js/web';
import { createStore, unwrap, reconcile } from 'solid-js/store';

// src/useQuery.ts
var QueryClientContext = createContext(void 0);
var useQueryClient = (queryClient) => {
  if (queryClient) {
    return queryClient;
  }
  const client = useContext(QueryClientContext);
  if (!client) {
    throw new Error("No QueryClient set, use QueryClientProvider to set one");
  }
  return client();
};
var QueryClientProvider = (props) => {
  createRenderEffect((unmount) => {
    unmount?.();
    props.client.mount();
    return props.client.unmount.bind(props.client);
  });
  onCleanup(() => props.client.unmount());
  return createComponent(QueryClientContext.Provider, {
    value: () => props.client,
    get children() {
      return props.children;
    }
  });
};
var IsRestoringContext = createContext(() => false);
var useIsRestoring = () => useContext(IsRestoringContext);
var IsRestoringProvider = IsRestoringContext.Provider;

// src/useBaseQuery.ts
function reconcileFn(store, result, reconcileOption, queryHash) {
  if (reconcileOption === false) return result;
  if (typeof reconcileOption === "function") {
    const newData2 = reconcileOption(store.data, result.data);
    return { ...result, data: newData2 };
  }
  let data = result.data;
  if (store.data === void 0) {
    try {
      data = structuredClone(data);
    } catch (error) {
      {
        if (error instanceof Error) {
          console.warn(
            `Unable to correctly reconcile data for query key: ${queryHash}. Possibly because the query data contains data structures that aren't supported by the 'structuredClone' algorithm. Consider using a callback function instead to manage the reconciliation manually.

 Error Received: ${error.name} - ${error.message}`
          );
        }
      }
    }
  }
  const newData = reconcile(data, { key: reconcileOption })(store.data);
  return { ...result, data: newData };
}
var hydratableObserverResult = (query, result) => {
  if (!isServer) return result;
  const obj = {
    ...unwrap(result),
    // During SSR, functions cannot be serialized, so we need to remove them
    // This is safe because we will add these functions back when the query is hydrated
    refetch: void 0
  };
  if ("fetchNextPage" in result) {
    obj.fetchNextPage = void 0;
    obj.fetchPreviousPage = void 0;
  }
  obj.hydrationData = {
    state: query.state,
    queryKey: query.queryKey,
    queryHash: query.queryHash,
    ...query.meta && { meta: query.meta }
  };
  return obj;
};
function useBaseQuery(options, Observer, queryClient) {
  const client = createMemo(() => useQueryClient(queryClient?.()));
  const isRestoring = useIsRestoring();
  let unsubscribeQueued = false;
  const defaultedOptions = createMemo(() => {
    const defaultOptions = client().defaultQueryOptions(options());
    defaultOptions._optimisticResults = isRestoring() ? "isRestoring" : "optimistic";
    defaultOptions.structuralSharing = false;
    if (isServer) {
      defaultOptions.retry = false;
      defaultOptions.throwOnError = true;
    }
    return defaultOptions;
  });
  const initialOptions = defaultedOptions();
  const [observer, setObserver] = createSignal(
    new Observer(client(), defaultedOptions())
  );
  let observerResult = observer().getOptimisticResult(defaultedOptions());
  const [state, setState] = createStore(observerResult);
  const createServerSubscriber = (resolve, reject) => {
    return observer().subscribe((result) => {
      notifyManager.batchCalls(() => {
        const query = observer().getCurrentQuery();
        const unwrappedResult = hydratableObserverResult(query, result);
        if (unwrappedResult.isError) {
          reject(unwrappedResult.error);
          unsubscribeIfQueued();
        } else {
          resolve(unwrappedResult);
          unsubscribeIfQueued();
        }
      })();
    });
  };
  const unsubscribeIfQueued = () => {
    if (unsubscribeQueued) {
      unsubscribe?.();
      unsubscribeQueued = false;
    }
  };
  const createClientSubscriber = () => {
    const obs = observer();
    return obs.subscribe((result) => {
      observerResult = result;
      queueMicrotask(() => {
        if (unsubscribe) {
          refetch();
        }
      });
    });
  };
  function setStateWithReconciliation(res) {
    const opts = observer().options;
    const reconcileOptions = opts.reconcile;
    setState((store) => {
      return reconcileFn(
        store,
        res,
        reconcileOptions === void 0 ? false : reconcileOptions,
        opts.queryHash
      );
    });
  }
  function createDeepSignal() {
    return [
      () => state,
      (v) => {
        const unwrapped = unwrap(state);
        if (typeof v === "function") {
          v = v(unwrapped);
        }
        if (v?.hydrationData) {
          const { hydrationData, ...rest } = v;
          v = rest;
        }
        setStateWithReconciliation(v);
      }
    ];
  }
  let unsubscribe = null;
  let resolver = null;
  const [queryResource, { refetch }] = createResource(
    () => {
      const obs = observer();
      return new Promise((resolve, reject) => {
        resolver = resolve;
        if (isServer) {
          unsubscribe = createServerSubscriber(resolve, reject);
        } else if (!unsubscribe && !isRestoring()) {
          unsubscribe = createClientSubscriber();
        }
        obs.updateResult();
        if (observerResult.isError && !observerResult.isFetching && !isRestoring() && shouldThrowError(obs.options.throwOnError, [
          observerResult.error,
          obs.getCurrentQuery()
        ])) {
          setStateWithReconciliation(observerResult);
          return reject(observerResult.error);
        }
        if (!observerResult.isLoading) {
          resolver = null;
          return resolve(
            hydratableObserverResult(obs.getCurrentQuery(), observerResult)
          );
        }
        setStateWithReconciliation(observerResult);
      });
    },
    {
      storage: createDeepSignal,
      get deferStream() {
        return options().deferStream;
      },
      /**
       * If this resource was populated on the server (either sync render, or streamed in over time), onHydrated
       * will be called. This is the point at which we can hydrate the query cache state, and setup the query subscriber.
       *
       * Leveraging onHydrated allows us to plug into the async and streaming support that solidjs resources already support.
       *
       * Note that this is only invoked on the client, for queries that were originally run on the server.
       */
      onHydrated(_k, info) {
        if (info.value && "hydrationData" in info.value) {
          hydrate(client(), {
            // @ts-expect-error - hydrationData is not correctly typed internally
            queries: [{ ...info.value.hydrationData }]
          });
        }
        if (unsubscribe) return;
        const newOptions = { ...initialOptions };
        if ((initialOptions.staleTime || !initialOptions.initialData) && info.value) {
          newOptions.refetchOnMount = false;
        }
        observer().setOptions(newOptions);
        setStateWithReconciliation(observer().getOptimisticResult(newOptions));
        unsubscribe = createClientSubscriber();
      }
    }
  );
  createComputed(
    on(
      client,
      (c) => {
        if (unsubscribe) {
          unsubscribe();
        }
        const newObserver = new Observer(c, defaultedOptions());
        unsubscribe = createClientSubscriber();
        setObserver(newObserver);
      },
      {
        defer: true
      }
    )
  );
  createComputed(
    on(
      isRestoring,
      (restoring) => {
        if (!restoring && !isServer) {
          refetch();
        }
      },
      { defer: true }
    )
  );
  onCleanup(() => {
    if (isServer && queryResource.loading) {
      unsubscribeQueued = true;
      return;
    }
    if (unsubscribe) {
      unsubscribe();
      unsubscribe = null;
    }
    if (resolver && !isServer) {
      resolver(observerResult);
      resolver = null;
    }
  });
  createComputed(
    on(
      [observer, defaultedOptions],
      ([obs, opts]) => {
        obs.setOptions(opts);
        setStateWithReconciliation(obs.getOptimisticResult(opts));
        refetch();
      },
      { defer: true }
    )
  );
  const handler = {
    get(target, prop) {
      if (prop === "data") {
        if (state.data !== void 0) {
          return queryResource.latest?.data;
        }
        return queryResource()?.data;
      }
      return Reflect.get(target, prop);
    }
  };
  return new Proxy(state, handler);
}

// src/useQuery.ts
function useQuery(options, queryClient) {
  return useBaseQuery(
    createMemo(() => options()),
    QueryObserver,
    queryClient
  );
}
function useInfiniteQuery(options, queryClient) {
  return useBaseQuery(
    createMemo(() => options()),
    InfiniteQueryObserver,
    queryClient
  );
}
function useMutation(options, queryClient) {
  const client = createMemo(() => useQueryClient(queryClient?.()));
  const observer = new MutationObserver(
    client(),
    options()
  );
  const mutate = (variables, mutateOptions) => {
    observer.mutate(variables, mutateOptions).catch(noop);
  };
  const [state, setState] = createStore({
    ...observer.getCurrentResult(),
    mutate,
    mutateAsync: observer.getCurrentResult().mutate
  });
  createComputed(() => {
    observer.setOptions(options());
  });
  createComputed(
    on(
      () => state.status,
      () => {
        if (state.isError && shouldThrowError(observer.options.throwOnError, [state.error])) {
          throw state.error;
        }
      }
    )
  );
  const unsubscribe = observer.subscribe((result) => {
    setState({
      ...result,
      mutate,
      mutateAsync: result.mutate
    });
  });
  onCleanup(unsubscribe);
  return state;
}
function useQueries(queriesOptions, queryClient) {
  const client = createMemo(() => useQueryClient(queryClient?.()));
  const isRestoring = useIsRestoring();
  const defaultedQueries = createMemo(
    () => queriesOptions().queries.map(
      (options) => mergeProps(
        client().defaultQueryOptions(options),
        {
          get _optimisticResults() {
            return isRestoring() ? "isRestoring" : "optimistic";
          }
        }
      )
    )
  );
  const observer = new QueriesObserver(
    client(),
    defaultedQueries(),
    queriesOptions().combine ? {
      combine: queriesOptions().combine
    } : void 0
  );
  const [state, setState] = createStore(
    observer.getOptimisticResult(
      defaultedQueries(),
      queriesOptions().combine
    )[1]()
  );
  createRenderEffect(
    on(
      () => queriesOptions().queries.length,
      () => setState(
        observer.getOptimisticResult(
          defaultedQueries(),
          queriesOptions().combine
        )[1]()
      )
    )
  );
  const dataResources = createMemo(
    on(
      () => state.length,
      () => state.map((queryRes) => {
        const dataPromise = () => new Promise((resolve) => {
          if (queryRes.isFetching && queryRes.isLoading) return;
          resolve(unwrap(queryRes.data));
        });
        return createResource(dataPromise);
      })
    )
  );
  batch(() => {
    const dataResources_ = dataResources();
    for (let index = 0; index < dataResources_.length; index++) {
      const dataResource = dataResources_[index];
      dataResource[1].mutate(() => unwrap(state[index].data));
      dataResource[1].refetch();
    }
  });
  let taskQueue = [];
  const subscribeToObserver = () => observer.subscribe((result) => {
    taskQueue.push(() => {
      batch(() => {
        const dataResources_ = dataResources();
        for (let index = 0; index < dataResources_.length; index++) {
          const dataResource = dataResources_[index];
          const unwrappedResult = { ...unwrap(result[index]) };
          setState(index, unwrap(unwrappedResult));
          dataResource[1].mutate(() => unwrap(state[index].data));
          dataResource[1].refetch();
        }
      });
    });
    queueMicrotask(() => {
      const taskToRun = taskQueue.pop();
      if (taskToRun) taskToRun();
      taskQueue = [];
    });
  });
  let unsubscribe = noop;
  createComputed((cleanup) => {
    cleanup?.();
    unsubscribe = isRestoring() ? noop : subscribeToObserver();
    return () => queueMicrotask(unsubscribe);
  });
  onCleanup(unsubscribe);
  onMount(() => {
    observer.setQueries(
      defaultedQueries(),
      queriesOptions().combine ? {
        combine: queriesOptions().combine
      } : void 0
    );
  });
  createComputed(() => {
    observer.setQueries(
      defaultedQueries(),
      queriesOptions().combine ? {
        combine: queriesOptions().combine
      } : void 0
    );
  });
  const handler = (index) => ({
    get(target, prop) {
      if (prop === "data") {
        return dataResources()[index][0]();
      }
      return Reflect.get(target, prop);
    }
  });
  const getProxies = () => state.map((s, index) => {
    return new Proxy(s, handler(index));
  });
  const [proxyState, setProxyState] = createStore(getProxies());
  createRenderEffect(() => setProxyState(getProxies()));
  return proxyState;
}
var QueryClient = class extends QueryClient$1 {
  constructor(config = {}) {
    super(config);
  }
};

// src/queryOptions.ts
function queryOptions(options) {
  return options;
}
function useIsFetching(filters, queryClient) {
  const client = createMemo(() => useQueryClient(queryClient?.()));
  const queryCache = createMemo(() => client().getQueryCache());
  const [fetches, setFetches] = createSignal(client().isFetching(filters?.()));
  const unsubscribe = queryCache().subscribe(() => {
    setFetches(client().isFetching(filters?.()));
  });
  onCleanup(unsubscribe);
  return fetches;
}

// src/infiniteQueryOptions.ts
function infiniteQueryOptions(options) {
  return options;
}
function useIsMutating(filters, queryClient) {
  const client = createMemo(() => useQueryClient(queryClient?.()));
  const mutationCache = createMemo(() => client().getMutationCache());
  const [mutations, setMutations] = createSignal(
    client().isMutating(filters?.())
  );
  const unsubscribe = mutationCache().subscribe((_result) => {
    setMutations(client().isMutating(filters?.()));
  });
  onCleanup(unsubscribe);
  return mutations;
}
function getResult(mutationCache, options) {
  return mutationCache.findAll(options.filters).map(
    (mutation) => options.select ? options.select(mutation) : mutation.state
  );
}
function useMutationState(options = () => ({}), queryClient) {
  const client = createMemo(() => useQueryClient(queryClient?.()));
  const mutationCache = createMemo(() => client().getMutationCache());
  const [result, setResult] = createSignal(
    getResult(mutationCache(), options())
  );
  createEffect(() => {
    const unsubscribe = mutationCache().subscribe(() => {
      const nextResult = replaceEqualDeep(
        result(),
        getResult(mutationCache(), options())
      );
      if (result() !== nextResult) {
        setResult(nextResult);
      }
    });
    onCleanup(unsubscribe);
  });
  return result;
}

// src/index.ts
var createQuery = useQuery;
var createInfiniteQuery = useInfiniteQuery;
var createMutation = useMutation;
var createQueries = useQueries;

export { IsRestoringProvider, QueryClient, QueryClientContext, QueryClientProvider, createInfiniteQuery, createMutation, createQueries, createQuery, infiniteQueryOptions, queryOptions, useInfiniteQuery, useIsFetching, useIsMutating, useIsRestoring, useMutation, useMutationState, useQueries, useQuery, useQueryClient };
