import type { AxiosError } from 'axios'; import { hashKey } from '@tanstack/query-core'; import type { QueryKey } from '@tanstack/query-core'; import type { QueryApiOptions } from '../query.api'; import { QueryApi } from '../query.api'; import type { ComposableStore } from './decorator'; /** * Container for creating runtime queries after store initialization. * The `@queryStore` decorator auto-discovers instances and binds the store reference. * * Use `add()` to create queries on demand (e.g., in response to user actions). * Queries are automatically set up with the store's QueryClientStore and * registered for disposal when the store is disposed. * * @example * ```typescript * @queryStore * class MyStore extends Store { * private queries = setupRuntimeQueries(); * * loadDetails(id: number) { * return this.queries.add
(() => ({ * queryKey: ['details', id], * queryFn: () => this.api?.getDetails(id), * }), ['details', id]); * } * } * ``` */ export class RuntimeQueries { /** @internal Bound by `@queryStore` decorator during initialize(). */ composableStore?: ComposableStore; /** * Creates and registers a runtime query. Deduplicates by key. * The query is immediately set up with the store's QueryClientStore. * * @param options - Query options, plain object or reactive function * @param key - Optional query key for dedup and retrieval via `get()` * @returns The created (or existing, if deduped) QueryApi instance */ add( options: QueryApiOptions | (() => QueryApiOptions), key?: QueryKey ): QueryApi { const state = this.composableStore?.queryStoreState; if (!state) { throw new Error( 'Store is not initialized. RuntimeQueries.add() can only be called after initialize().' ); } if (key) { const mapKey = hashKey(key); const existing = state.runtimeQueries.get(mapKey); if (existing) { return existing as QueryApi; } } const optionsFn = typeof options === 'function' ? options : () => options; const newQuery = new QueryApi(optionsFn); newQuery.setup(this.composableStore?.queryClientStore); if (key) { state.runtimeQueries.set(hashKey(key), newQuery); } state.managedApis.push(newQuery); return newQuery; } /** * Retrieves a previously created runtime query by key. * * @param key - The query key used when calling `add()` */ get(key: QueryKey): QueryApi | undefined { const state = this.composableStore?.queryStoreState; return state?.runtimeQueries.get(hashKey(key)) as QueryApi | undefined; } } /** * Factory for creating a RuntimeQueries container. * Declare as a class field — the `@queryStore` decorator auto-discovers and binds it. * * @example * ```typescript * @queryStore * class MyStore extends Store { * private queries = setupRuntimeQueries(); * * loadDetails(id: number) { * return this.queries.add
(() => ({ * queryKey: ['details', id], * queryFn: () => fetchDetails(id), * }), ['details', id]); * } * } * ``` */ export function setupRuntimeQueries(): RuntimeQueries { return new RuntimeQueries(); }