import { Store } from '@servicetitan/react-ioc'; import { AxiosError } from 'axios'; import type { MutationApiOptions } from './mutation.api'; import { MutationApi } from './mutation.api'; import { QueryClientStore } from './query-client.store'; import type { QueryApiOptions, IRefreshOnMountStore } from './query.api'; import { QueryApi } from './query.api'; import type { MutationKey, QueryKey } from '@tanstack/query-core'; /** * Abstract base class for creating MobX stores with TanStack Query integration. * Provides declarative data fetching with automatic caching, synchronization, and state management. * * For new stores, consider the `@queryStore` decorator + `query()`/`mutation()` helpers * from `@servicetitan/tanstack-query-mobx/composable` — it offers the same capabilities * with less boilerplate and more flexibility (composition over inheritance). * * @template T - The type of data returned by the primary query * @template TError - The error type (default: AxiosError) * * @remarks * **Key Features:** * - Extend this class and implement the `queryOptions` getter to configure your primary query * - The store automatically subscribes to the query client during initialization * - Query state is exposed as MobX observables (`data`, `isLoading`, `isError`, `initialized`, etc.) * - Supports multiple queries via {@link addQuery} and mutations via {@link addMutation} * - Automatic query deduplication and caching across component instances * - Data-driven refetching: queries automatically refetch when `queryKey` dependencies change * * **Observable State:** * - `data` - The query result (deep cloned to prevent mutations) * - `initialized` - True after first query completion (success or error) * - `isLoading` - True when fetching with no data yet * - `isFetching` - True whenever fetching (including refetches) * - `isError` - True when query failed * - `error` - The error object if failed * * **Lifecycle:** * 1. Constructor: Create the store * 2. `initialize()`: Setup queries and mutations (called by react-ioc) * 3. Query automatically runs based on `enabled` option (default: true) * 4. `dispose()`: Cleanup on unmount (called by react-ioc) * * @see {@link QueryApiOptions} for query configuration options * @see {@link QueryApi} for individual query management * @see {@link MutationApi} for mutation operations * @see README.md "Quick Start" for setup instructions * @see README.md "Common Patterns" for usage examples * * @example Basic store with single query * ```typescript * @injectable() * export class JobsStore extends QueryApiStore { * @inject(JobsApi) private api?: JobsApi; * * get queryOptions(): QueryApiOptions { * return { * queryKey: ['scheduling', 'jobs'], * queryFn: async () => (await this.api?.getJobs())?.data ?? [], * staleTime: 1000 * 60 * 5, // 5 minutes * }; * } * } * ``` * * @example Store with multiple queries and mutations * ```typescript * @injectable() * export class JobsStore extends QueryApiStore { * @inject(JobsApi) private api?: JobsApi; * @observable selectedJobId = 0; * * // Primary query * get queryOptions(): QueryApiOptions { * return { * queryKey: ['scheduling', 'jobs'], * queryFn: async () => (await this.api?.getJobs())?.data ?? [], * }; * } * * // Additional query - refetches when selectedJobId changes * jobDetails = this.addQuery(() => ({ * queryKey: ['scheduling', 'job', this.selectedJobId], * queryFn: async () => (await this.api?.getJob(this.selectedJobId))?.data, * enabled: this.selectedJobId > 0, * })); * * // Mutation with automatic invalidation * deleteJob = this.addMutation(() => ({ * mutationFn: async (arg) => await this.api?.deleteJob(arg.id), * invalidatedQueries: [['scheduling', 'jobs']], // Refetch jobs after delete * })); * } * ``` * * @example Using in a React component * ```typescript * const JobsList = observer(() => { * const [store] = useDependencies(JobsStore); * * if (!store.initialized) return ; * if (store.isError) return ; * if (store.isLoading) return ; * * return ( *
* {store.data?.map(job => )} * {store.isFetching && } *
* ); * }); * ``` */ export declare abstract class QueryApiStore extends Store implements IRefreshOnMountStore { private static callSuperDispose; query: QueryApi; overrideOptions?: () => Partial>; storesToRefreshOnMount?: () => (IRefreshOnMountStore | undefined)[]; protected queryClientStore?: QueryClientStore; protected disposables: (() => void)[]; private mutations; private queries; constructor(); /** * Becomes true after the first query attempt settles without any active fetch or invalidation. * Use this package-specific readiness flag to know when initial store setup has finished. */ get initialized(): boolean; /** * Mirrors TanStack Query's `isPending`. * True while the query has no settled result yet, including disabled queries that have never run. */ get isPending(): boolean; /** * Mirrors TanStack Query's `isLoading`. * True only while the first fetch is in flight (`isFetching && isPending`). */ get isLoading(): boolean; /** * Mirrors TanStack Query's `isFetching`. * True whenever the query function is executing, including the initial fetch and background refetches. */ get isFetching(): boolean; /** * Mirrors TanStack Query's `isRefetching`. * True during a background refetch after the query has already moved past the initial pending state. */ get isRefetching(): boolean; /** * Mirrors TanStack Query's `isSuccess` derived status flag. */ get isSuccess(): boolean; /** * Mirrors TanStack Query's `isError` derived status flag. */ get isError(): boolean; /** * Mirrors TanStack Query's `error` value. Defaults to `null` when no error is present. */ get error(): TError | null; /** * Mirrors TanStack Query's `refetch`. * @returns Promise that resolves with the query result */ get refetch(): (options?: import("@tanstack/query-core").RefetchOptions) => Promise>; /** * Function to invalidate and trigger a refetch of the query. * @param options - Optional invalidation options (debounce, dedupe) */ get invalidate(): (options?: import("..").QueryInvalidationOptions) => void; /** * Function to cancel an in-flight query request. */ get cancel(): () => void; /** * The last query data snapshot exposed by this store. * Successful results are deep-cloned before assignment so consumers do not mutate cached data by accident. */ get data(): T | undefined; /** * Query configuration options. Must be implemented by subclasses. * This getter is reactive - changes trigger query refetch. * * @example * ```typescript * get queryOptions(): QueryApiOptions { * return { * queryKey: ['scheduling', 'jobs', this.filters], * queryFn: async () => (await this.api?.getJobs(this.filters))?.data ?? [], * enabled: this.hasRequiredData, * }; * } * ``` */ get queryOptions(): QueryApiOptions; get refreshOnMount(): () => void; get refreshStoresOnMount(): () => void; initialize(): Promise; dispose(): typeof QueryApiStore.callSuperDispose & never; /** * Updates the cached query data without refetching from the server. * Triggers the onSuccess callback if configured. * * @param data - The new data to set * * @example * ```typescript * updateJobLocally(updatedJob: Job) { * const jobs = this.data ?? []; * const newJobs = jobs.map(j => j.id === updatedJob.id ? updatedJob : j); * this.updateQueryData(newJobs); * } * ``` */ updateQueryData: (data: T) => void; createMapKey: (key?: MutationKey | QueryKey) => string; /** * Adds a secondary query to the store. Most stores only need the primary `queryOptions` getter — * use `addQuery` only when your store needs to fetch from multiple endpoints. * * @template T - The type of data returned by this query * @param options - Function that returns query options (reactive) * @param key - Optional query key for retrieval via getQuery() * @param runSetup - Whether to immediately setup the query (default: false, waits for initialize()) * @returns The QueryApi instance for this query * * @example * ```typescript * statsQuery = this.addQuery(() => ({ * queryKey: ['scheduling', 'stats', this.filters], * queryFn: async () => (await this.api?.getStats(this.filters))?.data, * })); * * get stats() { * return this.statsQuery.data; * } * ``` */ addQuery: (options: () => QueryApiOptions, key?: QueryKey, runSetup?: boolean) => QueryApi>; /** * Retrieves a previously added query by its key. * * @template T - The type of data returned by the query * @param key - The query key used when calling addQuery() * @returns The QueryApi instance, or undefined if not found */ getQuery: (key: QueryKey) => QueryApi | undefined; /** * Adds a mutation to the store for data updates (create, update, delete operations). * * @template TData - The return type of the mutation function * @template TVariables - The type of variables passed to the mutation * @param options - Function that returns mutation options (reactive) * @param key - Optional mutation key for retrieval via getMutation() * @param runSetup - Whether to immediately setup the mutation (default: false, waits for initialize()) * @returns The MutationApi instance * * @example * ```typescript * createJob = this.addMutation(() => ({ * mutationFn: async (request) => (await this.api?.createJob(request))?.data, * invalidatedQueries: [['scheduling', 'jobs']], // Refetch jobs list after creation * onSuccess: (job) => toast.success(`Created ${job.title}`), * })); * * handleCreate = async (request: CreateJobRequest) => { * await this.createJob.runMutation(request); * }; * ``` */ addMutation: (options: () => MutationApiOptions, key?: MutationKey, runSetup?: boolean) => MutationApi>; /** * Retrieves a previously added mutation by its key. * * @template TData - The return type of the mutation * @template TVariables - The type of variables for the mutation * @param key - The mutation key used when calling addMutation() * @returns The MutationApi instance, or undefined if not found */ getMutation: (key: MutationKey) => MutationApi | undefined; } //# sourceMappingURL=query-api.store.d.ts.map