import type { AxiosError } from 'axios'; import { hashKey } from '@tanstack/query-core'; import type { MutationKey } from '@tanstack/query-core'; import type { MutationApiOptions } from '../mutation.api'; import { MutationApi } from '../mutation.api'; import type { ComposableStore } from './decorator'; /** * Container for creating runtime mutations after store initialization. * The `@queryStore` decorator auto-discovers instances and binds the store reference. * * Use `add()` to create mutations on demand (e.g., in response to user actions). * Mutations 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 mutations = setupRuntimeMutations(); * * setupDelete(entityId: number) { * return this.mutations.add(() => ({ * mutationFn: (args) => this.api?.delete(entityId, args), * }), ['delete', entityId]); * } * } * ``` */ export class RuntimeMutations { /** @internal Bound by `@queryStore` decorator during initialize(). */ composableStore?: ComposableStore; /** * Creates and registers a runtime mutation. Deduplicates by key. * The mutation is immediately set up with the store's QueryClientStore. * * @param options - Mutation options, plain object or reactive function * @param key - Optional mutation key for dedup and retrieval via `get()` * @returns The created (or existing, if deduped) MutationApi instance */ add( options: | MutationApiOptions | (() => MutationApiOptions), key?: MutationKey ): MutationApi { const state = this.composableStore?.queryStoreState; if (!state) { throw new Error( 'Store is not initialized. RuntimeMutations.add() can only be called after initialize().' ); } if (key) { const mapKey = hashKey(key); const existing = state.runtimeMutations.get(mapKey); if (existing) { return existing as MutationApi; } } const optionsFn = typeof options === 'function' ? options : () => options; const newMutation = new MutationApi(optionsFn); newMutation.setup(this.composableStore?.queryClientStore); if (key) { state.runtimeMutations.set(hashKey(key), newMutation); } state.managedApis.push(newMutation); return newMutation; } /** * Retrieves a previously created runtime mutation by key. * * @param key - The mutation key used when calling `add()` */ get( key: MutationKey ): MutationApi | undefined { const state = this.composableStore?.queryStoreState; return state?.runtimeMutations.get(hashKey(key)) as MutationApi | undefined; } } /** * Factory for creating a RuntimeMutations container. * Declare as a class field — the `@queryStore` decorator auto-discovers and binds it. * * @example * ```typescript * @queryStore * class MyStore extends Store { * private mutations = setupRuntimeMutations(); * * setupDelete(entityId: number) { * return this.mutations.add(() => ({ * mutationFn: (args) => this.api?.delete(entityId, args), * }), ['delete', entityId]); * } * } * ``` */ export function setupRuntimeMutations(): RuntimeMutations { return new RuntimeMutations(); }