- Consolidated duplicate UndoManagers to single instance - Fixed connection promise to only resolve on 'connected' status - Fixed WebSocketProvider import (WebsocketProvider) - Added proper doc.destroy() cleanup - Renamed isPresenceInitialized property to avoid conflict Co-Authored-By: Paperclip <noreply@paperclip.ing>
522 lines
16 KiB
JavaScript
522 lines
16 KiB
JavaScript
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) {
|
|
}
|
|
}
|
|
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;
|
|
defaultOptions.experimental_prefetchInRender = 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 (result.data !== void 0 && 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;
|
|
}
|
|
|
|
// src/mutationOptions.ts
|
|
function mutationOptions(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, useIsFetching as createIsFetching, useIsMutating as createIsMutating, createMutation, useMutationState as createMutationState, createQueries, createQuery, infiniteQueryOptions, mutationOptions, queryOptions, useInfiniteQuery, useIsFetching, useIsMutating, useIsRestoring, useMutation, useMutationState, useQueries, useQuery, useQueryClient };
|