UNPKG

@tanstack/db-collections

Version:

A collection for (aspirationally) every way of loading your data

148 lines (147 loc) 4.35 kB
import { QueryObserver } from "@tanstack/query-core"; function queryCollectionOptions(config) { const { queryKey, queryFn, queryClient, enabled, refetchInterval, retry, retryDelay, staleTime, getKey, onInsert, onUpdate, onDelete, ...baseCollectionConfig } = config; if (!queryKey) { throw new Error(`[QueryCollection] queryKey must be provided.`); } if (!queryFn) { throw new Error(`[QueryCollection] queryFn must be provided.`); } if (!queryClient) { throw new Error(`[QueryCollection] queryClient must be provided.`); } if (!getKey) { throw new Error(`[QueryCollection] getKey must be provided.`); } const internalSync = (params) => { const { begin, write, commit, collection } = params; const observerOptions = { queryKey, queryFn, enabled, refetchInterval, retry, retryDelay, staleTime, structuralSharing: true, notifyOnChangeProps: `all` }; const localObserver = new QueryObserver(queryClient, observerOptions); const actualUnsubscribeFn = localObserver.subscribe((result) => { if (result.isSuccess) { const newItemsArray = result.data; if (!Array.isArray(newItemsArray) || newItemsArray.some((item) => typeof item !== `object`)) { console.error( `[QueryCollection] queryFn did not return an array of objects. Skipping update.`, newItemsArray ); return; } const currentSyncedItems = new Map(collection.syncedData); const newItemsMap = /* @__PURE__ */ new Map(); newItemsArray.forEach((item) => { const key = getKey(item); newItemsMap.set(key, item); }); begin(); const shallowEqual = (obj1, obj2) => { const keys1 = Object.keys(obj1); const keys2 = Object.keys(obj2); if (keys1.length !== keys2.length) return false; return keys1.every((key) => { if (typeof obj1[key] === `function`) return true; if (typeof obj1[key] === `object` && obj1[key] !== null) { return obj1[key] === obj2[key]; } return obj1[key] === obj2[key]; }); }; currentSyncedItems.forEach((oldItem, key) => { const newItem = newItemsMap.get(key); if (!newItem) { write({ type: `delete`, value: oldItem }); } else if (!shallowEqual( oldItem, newItem )) { write({ type: `update`, value: newItem }); } }); newItemsMap.forEach((newItem, key) => { if (!currentSyncedItems.has(key)) { write({ type: `insert`, value: newItem }); } }); commit(); } else if (result.isError) { console.error( `[QueryCollection] Error observing query ${String(queryKey)}:`, result.error ); } }); return async () => { actualUnsubscribeFn(); await queryClient.cancelQueries({ queryKey }); queryClient.removeQueries({ queryKey }); }; }; const refetch = async () => { return queryClient.refetchQueries({ queryKey }); }; const wrappedOnInsert = onInsert ? async (params) => { const handlerResult = await onInsert(params) ?? {}; const shouldRefetch = handlerResult.refetch !== false; if (shouldRefetch) { await refetch(); } return handlerResult; } : void 0; const wrappedOnUpdate = onUpdate ? async (params) => { const handlerResult = await onUpdate(params) ?? {}; const shouldRefetch = handlerResult.refetch !== false; if (shouldRefetch) { await refetch(); } return handlerResult; } : void 0; const wrappedOnDelete = onDelete ? async (params) => { const handlerResult = await onDelete(params) ?? {}; const shouldRefetch = handlerResult.refetch !== false; if (shouldRefetch) { await refetch(); } return handlerResult; } : void 0; return { ...baseCollectionConfig, getKey, sync: { sync: internalSync }, onInsert: wrappedOnInsert, onUpdate: wrappedOnUpdate, onDelete: wrappedOnDelete, utils: { refetch } }; } export { queryCollectionOptions }; //# sourceMappingURL=query.js.map