UNPKG

@jay-js/system

Version:

A powerful and flexible TypeScript library for UI, state management, lazy loading, routing and managing draggable elements in modern web applications.

243 lines 8.6 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { state } from "../state/core/state.js"; import { effect } from "../state/utils/helpers.js"; import { queryCache } from "./cache.js"; import { cancelFetch, defaultRetryDelay, executeFetch, executeWithRetry, resolveQueryKey, setupFocusRefetch, setupReconnectRefetch, } from "./utils.js"; /** * Default query options */ const DEFAULT_OPTIONS = { enabled: true, staleTime: 0, cacheTime: 300000, refetchOnFocus: false, refetchOnReconnect: false, refetchInterval: false, retry: 3, retryDelay: defaultRetryDelay, }; /** * Creates a reactive query for async data fetching with caching and automatic revalidation * * @param key Unique identifier for the query, can be reactive function * @param fetcher Async function to fetch data * @param options Query configuration options * @returns Query store with reactive states and control methods * * @example * Basic usage: * ```typescript * const usersQuery = query('users', async () => { * const res = await fetch('/api/users'); * return res.json(); * }); * * // Use in component * effect(() => { * if (usersQuery.data.value) { * console.log(usersQuery.data.value); * } * }); * ``` * * @example * With options: * ```typescript * const todosQuery = query('todos', fetchTodos, { * staleTime: 5000, * cacheTime: 300000, * refetchOnFocus: true, * retry: 3 * }); * ``` * * @example * Reactive key: * ```typescript * const userId = state(1); * const userQuery = query( * () => `user-${userId.value}`, * async () => { * const res = await fetch(`/api/users/${userId.value}`); * return res.json(); * } * ); * ``` */ export function query(key, fetcher, options = {}) { var _a; const opts = Object.assign(Object.assign({}, DEFAULT_OPTIONS), options); const internalState = state({ data: ((_a = options.initialData) !== null && _a !== void 0 ? _a : null), error: null, isLoading: false, isFetching: false, isError: false, isSuccess: options.initialData !== undefined, status: (options.initialData !== undefined ? "success" : "idle"), }); let cleanupFns = []; let refetchInterval = null; const currentKey = () => resolveQueryKey(key); const execute = (...args_1) => __awaiter(this, [...args_1], void 0, function* (isRefetch = false) { var _a, _b; const queryKey = currentKey(); if (!isRefetch) { // internalState.value.isLoading = true; internalState.set((currentState) => { return Object.assign(Object.assign({}, currentState), { isLoading: true }); }); } internalState.set((currentState) => { return Object.assign(Object.assign({}, currentState), { isFetching: true, status: "loading" }); }); try { const data = yield executeFetch(queryKey, (signal) => executeWithRetry(fetcher, opts.retry, opts.retryDelay, signal), () => { internalState.set((currentState) => { return Object.assign(Object.assign({}, currentState), { isFetching: false }); }); }); internalState.set((currentState) => { return Object.assign(Object.assign({}, currentState), { data: data, error: null, isError: false, isSuccess: true, status: "success" }); }); queryCache.set(queryKey, data, opts.cacheTime); (_a = opts.onSuccess) === null || _a === void 0 ? void 0 : _a.call(opts, data); } catch (error) { internalState.set((currentState) => { return Object.assign(Object.assign({}, currentState), { error: error, isError: true, isSuccess: false, status: "error" }); }); (_b = opts.onError) === null || _b === void 0 ? void 0 : _b.call(opts, error); } finally { internalState.set((currentState) => { return Object.assign(Object.assign({}, currentState), { isLoading: false, isFetching: false }); }); } }); const refetch = () => __awaiter(this, void 0, void 0, function* () { yield execute(true); }); const invalidate = () => { const queryKey = currentKey(); queryCache.delete(queryKey); execute(true); }; const reset = () => { var _a; internalState.set({ data: ((_a = options.initialData) !== null && _a !== void 0 ? _a : null), error: null, isLoading: false, isFetching: false, isError: false, isSuccess: options.initialData !== undefined, status: (options.initialData !== undefined ? "success" : "idle"), }); }; const cancel = () => { const queryKey = currentKey(); cancelFetch(queryKey); internalState.set((currentState) => { return Object.assign(Object.assign({}, currentState), { isFetching: false }); }); }; const initialize = () => { const queryKey = currentKey(); const cached = queryCache.get(queryKey); const isStale = cached ? queryCache.isStale(queryKey, opts.staleTime) : true; if (cached && !isStale) { internalState.set((currentState) => { return Object.assign(Object.assign({}, currentState), { data: cached.data, isSuccess: true, status: "success" }); }); } const hasInitialData = options.initialData !== undefined; if (opts.enabled) { if (!cached || queryCache.isStale(queryKey, opts.staleTime)) { if (!hasInitialData || cached) { execute(false); } } } const unsubscribeListener = queryCache.onChange(queryKey, (data) => { internalState.set((currentState) => { return Object.assign(Object.assign({}, currentState), { data: data, isSuccess: true, status: "success" }); }); }); cleanupFns.push(unsubscribeListener); if (opts.refetchOnFocus) { cleanupFns.push(setupFocusRefetch(() => { if (queryCache.isStale(queryKey, opts.staleTime)) { refetch(); } })); } if (opts.refetchOnReconnect) { cleanupFns.push(setupReconnectRefetch(() => refetch())); } if (opts.refetchInterval && typeof opts.refetchInterval === "number") { refetchInterval = setInterval(() => refetch(), opts.refetchInterval); } queryCache.subscribe(queryKey); }; const cleanup = () => { const queryKey = currentKey(); for (const fn of cleanupFns) { fn(); } cleanupFns = []; if (refetchInterval) { clearInterval(refetchInterval); refetchInterval = null; } queryCache.unsubscribe(queryKey); }; if (typeof key === "function") { effect(() => { cleanup(); initialize(); }); } else { initialize(); } return { get data() { return internalState.value.data; }, get error() { return internalState.value.error; }, get isLoading() { return internalState.value.isLoading; }, get isFetching() { return internalState.value.isFetching; }, get isError() { return internalState.value.isError; }, get isSuccess() { return internalState.value.isSuccess; }, get status() { return internalState.value.status; }, refetch, invalidate, reset, cancel, dispose() { cleanup(); }, }; } //# sourceMappingURL=query.js.map