stunk
Version:
Stunk is a lightweight, framework-agnostic state management library for JavaScript and TypeScript. It uses chunk-based state units for efficient updates, reactivity, and performance optimization in React, Vue(WIP), Svelte(Coming soon), and Vanilla JS/TS a
668 lines (651 loc) • 28.9 kB
TypeScript
type Subscriber<T> = (newValue: T) => void;
type Middleware<T> = (value: T) => T | undefined;
type NamedMiddleware<T> = {
name?: string;
fn: Middleware<T>;
};
interface ChunkConfig<T> {
name?: string;
middleware?: (Middleware<T> | NamedMiddleware<T>)[];
/**
* When true, setting a value with keys not present in the initial
* shape throws an error in development. Useful for catching accidental
* shape mutations early. Has no effect in production. (default: false)
*/
strict?: boolean;
}
interface Chunk<T> {
/** Get the current value of the chunk. */
get: () => T;
/** Peek at the current value without tracking dependencies. */
peek: () => T;
/** Set a new value for the chunk & Update existing value efficiently. */
set: (newValueOrUpdater: T | ((currentValue: T) => T)) => void;
/** Subscribe to changes in the chunk. Returns an unsubscribe function. */
subscribe: (callback: Subscriber<T>) => () => void;
/** Create a derived chunk based on this chunk's value. */
derive: <D>(fn: (value: T) => D) => ReadOnlyChunk<D>;
/** Reset the chunk to its initial value. */
reset: () => void;
/** Destroy the chunk and all its subscribers. */
destroy: () => void;
}
interface ReadOnlyChunk<T> extends Omit<Chunk<T>, 'set' | 'reset'> {
derive: <D>(fn: (value: T) => D) => ReadOnlyChunk<D>;
}
/**
* Groups multiple chunk updates into a single notification pass.
*
* Without batching, each `set()` call notifies subscribers immediately.
* Inside a `batch()`, all updates are collected and subscribers are notified
* once after the callback completes — even if multiple chunks were updated.
*
* Batches can be nested safely. Notifications only flush when the outermost
* batch completes.
*
* @param callback - A function containing one or more chunk `set()` calls.
*
* @example
* const x = chunk(0);
* const y = chunk(0);
*
* batch(() => {
* x.set(1);
* y.set(2);
* });
* // Subscribers of x and y are each notified once, not twice.
*/
declare function batch(callback: () => void): void;
/**
* Creates a reactive state unit — the core primitive of Stunk.
*
* A chunk holds a single value and notifies all subscribers whenever that
* value changes. Values are compared by reference (`===`) for primitives and
* by the result of middleware processing for objects — so setting the same
* value twice does not trigger subscribers.
*
* @param initialValue - The starting value. Cannot be `undefined`.
* @param config - Optional configuration for naming, middleware, and strict mode.
* @returns A `Chunk<T>` with `get()`, `set()`, `peek()`, `subscribe()`, `derive()`, `reset()`, and `destroy()`.
*
* @throws If `initialValue` is `undefined`.
*
* @example
* const count = chunk(0);
* count.get(); // 0
* count.set(1); // subscribers notified
* count.set(n => n + 1); // updater function
* count.reset(); // back to 0
*
* @example
* // Named chunk with strict mode — throws on unknown keys in dev
* const user = chunk({ name: 'Alice', age: 30 }, { name: 'user', strict: true });
*
* @example
* // With middleware
* const positive = chunk(0, {
* middleware: [nonNegativeValidator]
* });
*/
declare function chunk<T>(initialValue: T, config?: ChunkConfig<T>): Chunk<T>;
interface ChunkMeta {
name: string;
id: number;
}
declare function isValidChunkValue(value: unknown): boolean;
declare function isChunk<T>(value: unknown): value is Chunk<T>;
declare function getChunkMeta<T>(chunk: Chunk<T>): ChunkMeta | undefined;
interface Computed<T> extends ReadOnlyChunk<T> {
/** Checks if the computed value needs to be recalculated due to dependency changes. */
isDirty: () => boolean;
/** Manually forces recalculation of the computed value from its dependencies. */
recompute: () => void;
}
/**
* Creates a derived value that automatically tracks its dependencies and
* recomputes lazily when any of them change.
*
* Dependencies are discovered automatically — any chunk whose `.get()` is
* called inside `computeFn` is tracked. Use `.peek()` inside the function
* to read a value without tracking it as a dependency.
*
* The computed value is cached and only recalculated when a dependency
* changes and the value is accessed (lazy) or when active subscribers exist
* (eager). Object values are compared with shallow equality to prevent
* unnecessary subscriber notifications.
*
* @param computeFn - A pure function that derives the computed value.
* @returns A read-only `Computed<T>` with `isDirty()`, `recompute()`, `derive()`, `subscribe()`, `peek()`, and `destroy()`.
*/
declare function computed<T>(computeFn: () => T): Computed<T>;
interface SelectOptions {
/**
* When `true`, uses shallow equality to compare selected values.
* Prevents unnecessary subscriber notifications when the selected
* object is a new reference but has the same property values.
*/
useShallowEqual?: boolean;
}
/**
* Creates a read-only derived chunk that tracks a slice of a source chunk.
*
* Only notifies subscribers when the selected value actually changes —
* updates to unselected parts of the source are ignored.
*
* @param sourceChunk - The chunk to select from.
* @param selector - A function that extracts the desired slice.
* @param options.useShallowEqual - When `true`, uses shallow equality to
* compare selected values — prevents unnecessary updates for objects.
* @returns A `ReadOnlyChunk<S>` that updates only when the selected value changes.
*
* @example
* const user = chunk({ name: 'Alice', age: 30 });
* const name = select(user, u => u.name);
* name.get(); // 'Alice'
*
* // age changes — name subscribers are NOT notified
* user.set({ name: 'Alice', age: 31 });
*
* @example
* // Shallow equality — prevents updates when object values are the same
* const details = select(user, u => u.details, { useShallowEqual: true });
*/
declare function select<T, S>(sourceChunk: Chunk<T> | ReadOnlyChunk<T>, selector: (value: T) => S, options?: SelectOptions): ReadOnlyChunk<S>;
/**
* Middleware that logs every value passed to `set()` to the console.
*
* @example
* const count = chunk(0, { middleware: [logger()] });
* count.set(5); // logs: "Setting value: 5"
*/
declare function logger<T>(): Middleware<T>;
/**
* Middleware that throws if a numeric value is set below zero.
*
* @example
* const balance = chunk(100, { middleware: [nonNegativeValidator] });
* balance.set(-1); // throws: "Value must be non-negative!"
*/
declare const nonNegativeValidator: Middleware<number>;
interface ChunkWithHistory<T> extends Chunk<T> {
/** Reverts to the previous state (if available). */
undo: () => void;
/** Moves to the next state (if available). */
redo: () => void;
/** Returns true if there is a previous state to revert to. */
canUndo: () => boolean;
/** Returns true if there is a next state to move to. */
canRedo: () => boolean;
/** Returns an array of all the values in the history. */
getHistory: () => T[];
/** Clears the history, keeping only the current value. */
clearHistory: () => void;
}
/**
* Wraps a chunk with undo/redo history tracking.
*
* Every `set()` call is recorded. `undo()` and `redo()` move through the stack.
* Branching is supported — calling `set()` after `undo()` discards forward history.
*
* @param baseChunk - The chunk to wrap.
* @param options.maxHistory - Max entries to keep (default: 100).
* @param options.skipDuplicates - `true` skips strictly equal values.
* `'shallow'` also skips shallowly equal objects.
*
* @example
* const count = chunk(0);
* const tracked = history(count);
* tracked.set(1); tracked.set(2);
* tracked.undo(); // 1
* tracked.redo(); // 2
*/
declare function history<T>(baseChunk: Chunk<T>, options?: {
maxHistory?: number;
/**
* true — skip entries that are strictly equal (===) to the current value.
* 'shallow' — also skip entries that are shallowly equal to the current value.
*/
skipDuplicates?: boolean | "shallow";
}): ChunkWithHistory<T>;
interface PersistOptions<T> {
/** Storage key (required). */
key: string;
/** Storage engine (default: localStorage). */
storage?: Storage;
/** Serialize value to string (default: JSON.stringify). */
serialize?: (value: T) => string;
/** Deserialize string to value (default: JSON.parse). */
deserialize?: (value: string) => T;
/** Called on load/save errors and type mismatches. */
onError?: (error: Error, operation: 'load' | 'save') => void;
}
interface PersistedChunk<T> extends Chunk<T> {
/** Remove the persisted key from storage without destroying the chunk. */
clearStorage: () => void;
}
/**
* Wraps a chunk with automatic persistence to a storage engine.
*
* Loads any saved value on creation. Saves on every `set()`.
* Gracefully disabled in SSR when no storage is available.
*
* @param baseChunk - The chunk to wrap.
* @param options.key - Storage key (required).
* @param options.storage - Storage engine (default: `localStorage`).
* @param options.serialize - Custom serializer (default: `JSON.stringify`).
* @param options.deserialize - Custom deserializer (default: `JSON.parse`).
* @param options.onError - Called on load/save errors or type mismatches.
*
* @example
* const user = chunk({ name: 'Alice' });
* const persisted = persist(user, { key: 'user' });
* persisted.set({ name: 'Bob' }); // saved to localStorage
* persisted.clearStorage(); // removes the key
*/
declare function persist<T>(baseChunk: Chunk<T>, options: PersistOptions<T>): PersistedChunk<T>;
declare const index$1_history: typeof history;
declare const index$1_logger: typeof logger;
declare const index$1_nonNegativeValidator: typeof nonNegativeValidator;
declare const index$1_persist: typeof persist;
declare namespace index$1 {
export { index$1_history as history, index$1_logger as logger, index$1_nonNegativeValidator as nonNegativeValidator, index$1_persist as persist };
}
interface AsyncState<T, E extends Error> {
loading: boolean;
error: E | null;
data: T | null;
lastFetched?: number;
/** True when showing previous data while new data is loading (keepPreviousData: true) */
isPlaceholderData?: boolean;
}
interface PaginationState {
page: number;
pageSize: number;
total?: number;
hasMore?: boolean;
/** Current cursor — only populated when using cursor-based pagination */
cursor?: string;
}
interface AsyncStateWithPagination<T, E extends Error> extends AsyncState<T, E> {
pagination?: PaginationState;
}
interface ParamAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}> extends AsyncChunk<T, E> {
setParams: (params: Partial<Record<keyof P, P[keyof P] | null>>) => void;
clearParams: () => void;
reload: (params?: Partial<P>) => Promise<void>;
refresh: (params?: Partial<P>) => Promise<void>;
}
interface PaginatedParamAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}> extends PaginatedAsyncChunk<T, E> {
setParams: (params: Partial<Record<keyof P, P[keyof P] | null>>) => void;
clearParams: () => void;
reload: (params?: Partial<P>) => Promise<void>;
refresh: (params?: Partial<P>) => Promise<void>;
}
interface FetcherResponse<T> {
data: T;
total?: number;
hasMore?: boolean;
/** Next cursor — only used when `pagination.cursorMode` is configured */
cursor?: string;
}
interface CursorModeConfig<T> {
/**
* Extracts the next cursor from a fetch response. Return `undefined`
* when there are no more pages.
*/
getNextCursor: (response: FetcherResponse<T>) => string | undefined;
}
interface AsyncChunkOptions<T, E extends Error = Error, P extends Record<string, any> = {}> {
/** Deduplication key — concurrent calls with the same key share one in-flight request */
key?: string;
/** Seed data shown before the first fetch completes */
initialData?: T | null;
/** Disable fetching until ready — pass a function for dynamic evaluation */
enabled?: boolean | ((params: Partial<P>) => boolean);
/** Called after every successful fetch */
onSuccess?: (data: T) => void;
/** Called when all retries are exhausted */
onError?: (error: E) => void;
/** Number of retries on failure (default: 0) */
retryCount?: number;
/** Delay in ms between retries (default: 1000) */
retryDelay?: number;
/** Show previous data while refetching — prevents UI flicker on param changes (default: false) */
keepPreviousData?: boolean;
/**
* Clear data to null immediately when params change via setParams(), before
* the new fetch resolves. Eliminates stale data flash when navigating between
* detail views that share a single chunk. (default: false)
*
* @example
* // jobDetailChunk will show null (loading state) immediately when ref changes,
* // instead of showing the previous job's data while the new one loads.
* export const jobDetailChunk = asyncChunk(
* (params: { ref: string }) => jobsApi.getJobDetail(params.ref),
* { staleTime: 0, clearOnParamChange: true }
* );
*/
clearOnParamChange?: boolean;
/** Time in ms before data is considered stale (default: 0) */
staleTime?: number;
/** Time in ms to cache data after last subscriber leaves (default: 300_000) */
cacheTime?: number;
/** Auto-refetch interval in ms */
refetchInterval?: number;
/** Refetch when window regains focus (default: false) */
refetchOnWindowFocus?: boolean;
/**
* When true, useAsyncChunk gives each calling component its own
* independent instance of this chunk instead of sharing the module-level
* singleton. Use for parameterized/filtered data (paginated lists, search
* results) where multiple simultaneous consumers must not share state.
* Leave false (default) for true app-wide singletons (current user,
* wallet balance, notification list) that must stay in sync everywhere.
* (default: false)
*/
scoped?: boolean;
pagination?: {
/** Initial page number (default: 1) */
initialPage?: number;
/** Items per page (default: 10) */
pageSize?: number;
/** Replace data on each page load, or accumulate for infinite scroll (default: 'replace') */
mode?: 'replace' | 'accumulate';
/**
* Switches the chunk to cursor-based pagination. When set, `page` is
* ignored — the chunk tracks an opaque `cursor` string supplied by
* your fetcher's response instead of an incrementing page number.
*/
cursorMode?: CursorModeConfig<T>;
};
}
interface AsyncChunk<T, E extends Error = Error> extends Chunk<AsyncStateWithPagination<T, E>> {
/** Force a fresh fetch, ignoring stale time */
reload: (params?: any) => Promise<void>;
/** Fetch only if data is stale — respects staleTime */
refresh: (params?: any) => Promise<void>;
/** Update data directly without a network request */
mutate: (mutator: (currentData: T | null) => T | null) => void;
/** Reset to initial state and re-fetch */
reset: (refetch?: boolean) => void;
/** Safe cleanup — only tears down if no active subscribers remain */
cleanup: () => void;
/** Force cleanup regardless of subscriber count */
forceCleanup: () => void;
/** Clear all current params and refetch */
clearParams: () => void;
/** Cancel any in-flight request and set loading to false */
cancel: () => void;
}
interface PaginatedAsyncChunk<T, E extends Error = Error> extends AsyncChunk<T, E> {
/** Load the next page */
nextPage: () => Promise<void>;
/** Load the previous page */
prevPage: () => Promise<void>;
/** Jump to a specific page */
goToPage: (page: number) => Promise<void>;
/** Reset pagination to page 1 and re-fetch */
resetPagination: () => Promise<void>;
}
declare function asyncChunk<T, E extends Error = Error>(fetcher: () => Promise<T | FetcherResponse<T>>, options?: Omit<AsyncChunkOptions<T, E>, 'pagination'>): AsyncChunk<T, E>;
declare function asyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(fetcher: (params: P) => Promise<T | FetcherResponse<T>>, options?: Omit<AsyncChunkOptions<T, E, P>, 'pagination'>): ParamAsyncChunk<T, E, P>;
/**
* Creates a paginated async state unit — always returns `PaginatedParamAsyncChunk`
* with `nextPage`/`prevPage`/`goToPage`/`resetPagination`.
*/
declare function paginatedAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(fetcher: (params: P & {
page?: number;
pageSize: number;
cursor?: string;
}) => Promise<FetcherResponse<T>>, options: AsyncChunkOptions<T, E, P> & {
pagination: NonNullable<AsyncChunkOptions<T, E, P>['pagination']>;
}): PaginatedParamAsyncChunk<T, E, P>;
type InfiniteAsyncChunkOptions<T, E extends Error = Error> = Omit<AsyncChunkOptions<T[], E>, 'pagination'> & {
/** Items per page (default: 10) */
pageSize?: number;
/**
* Use cursor-based pagination instead of page-number based.
* Required when the backend returns an opaque cursor (e.g. base64-encoded)
* rather than supporting page numbers directly.
*/
cursorMode?: CursorModeConfig<T[]>;
};
type InfiniteAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}> = PaginatedParamAsyncChunk<T[], E, P & {
page?: number;
pageSize: number;
cursor?: string;
}>;
/**
* Creates an infinite scroll async chunk that accumulates pages.
*
* Supports both page-number pagination (default) and cursor-based pagination
* via `options.cursorMode` — use cursor mode when your backend returns an
* opaque `nextCursor` rather than working with page numbers.
*
* @example
* // Page-based (default)
* const posts = infiniteAsyncChunk(
* async ({ page, pageSize }) => fetchPosts({ page, pageSize }),
* { pageSize: 20 }
* );
*
* @example
* // Cursor-based
* const conversations = infiniteAsyncChunk(
* async ({ cursor, pageSize }) => {
* const res = await listConversations({ cursor, limit: pageSize });
* return { data: res.data, hasMore: res.hasMore, cursor: res.nextCursor };
* },
* { pageSize: 20, cursorMode: { getNextCursor: (res) => res.cursor } }
* );
*/
declare function infiniteAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(fetcher: (params: P & {
page?: number;
pageSize: number;
cursor?: string;
}) => Promise<{
data: T[];
hasMore?: boolean;
total?: number;
cursor?: string;
}>, options?: InfiniteAsyncChunkOptions<T, E>): InfiniteAsyncChunk<T, E, P>;
type InferAsyncData<T> = T extends AsyncChunk<infer U, Error> ? U : never;
type CombinedData<T extends Record<string, AsyncChunk<any>>> = {
[K in keyof T]: InferAsyncData<T[K]> | null;
};
type CombinedState<T extends Record<string, AsyncChunk<any>>> = {
loading: boolean;
error: Error | null;
errors: Partial<{
[K in keyof T]: Error;
}>;
data: CombinedData<T>;
};
/**
* Combines multiple async chunks into a single unified state chunk.
*
* The result tracks `loading` (true if any chunk is loading), `error` (first
* error encountered), `errors` (per-chunk errors), and `data` (per-chunk data).
*
* @param chunks - A record of named `AsyncChunk` instances.
* @returns A `Chunk<CombinedState<T>>` that reflects the live state of all inputs.
*
* @example
* const combined = combineAsyncChunks({ user: userChunk, posts: postsChunk });
* combined.get(); // { loading, error, errors, data: { user, posts } }
*/
declare function combineAsyncChunks<T extends Record<string, AsyncChunk<any>>>(chunks: T): Chunk<CombinedState<T>>;
interface GlobalQueryConfig {
/**
* Default configuration applied to all asyncChunk instances.
* Per-chunk options always override these defaults.
*/
query?: {
/** Time in ms before data is considered stale (default: 0) */
staleTime?: number;
/** Time in ms to cache data after last subscriber leaves (default: 300_000) */
cacheTime?: number;
/** Auto-refetch interval in ms */
refetchInterval?: number;
/** Refetch when window regains focus (default: false) */
refetchOnWindowFocus?: boolean;
/** Number of retries on failure (default: 0) */
retryCount?: number;
/** Delay in ms between retries (default: 1000) */
retryDelay?: number;
/** Global error handler — called when all retries are exhausted */
onError?: (error: Error) => void;
/** Global success handler — called after every successful fetch */
onSuccess?: (data: unknown) => void;
};
/**
* Default configuration applied to all mutation instances.
* Reserved for when mutation() ships — per-mutation options always override.
*/
mutation?: {
/** Global error handler for mutations */
onError?: (error: Error) => void;
/** Global success handler for mutations */
onSuccess?: (data: unknown) => void;
};
}
/**
* Configures global defaults for all `asyncChunk` and `mutation` instances.
*
* Call this once at app entry — before any `asyncChunk` is created.
* Per-chunk options always take precedence over these defaults.
*
* @param config.query - Defaults for all async chunks (staleTime, retryCount, onError, etc.)
* @param config.mutation - Defaults for all mutations (onError, onSuccess)
*
* @example
* import { configureQuery } from "stunk/query";
*
* configureQuery({
* query: {
* staleTime: 30_000,
* retryCount: 3,
* refetchOnWindowFocus: true,
* onError: (err) => toast.error(err.message),
* },
* mutation: {
* onError: (err) => toast.error(err.message),
* onSuccess: () => toast.success("Done!"),
* },
* });
*/
declare function configureQuery(config: GlobalQueryConfig): void;
/**
* Returns the current global query config.
* Used internally by asyncChunk and mutation to read defaults.
*/
declare function getGlobalQueryConfig(): GlobalQueryConfig;
/**
* Resets the global config back to defaults.
* Primarily useful in tests to avoid config bleed between test cases.
*
* @example
* afterEach(() => resetQueryConfig());
*/
declare function resetQueryConfig(): void;
type MutationFn<TData, TVariables> = (variables: TVariables) => Promise<TData>;
interface MutationOptions<TData, TError extends Error = Error, TVariables = void> {
/** Chunks to automatically reload after a successful mutation */
invalidates?: AsyncChunk<any, any>[];
/** Called after a successful mutation with the returned data and original variables */
onSuccess?: (data: TData, variables: TVariables) => void;
/** Called when the mutation fails with the error and original variables */
onError?: (error: TError, variables: TVariables) => void;
/** Called after every attempt — success or failure — useful for unconditional cleanup */
onSettled?: (data: TData | null, error: TError | null, variables: TVariables) => void;
}
interface MutationState<TData, TError extends Error = Error> {
/** True while the mutation is in progress */
loading: boolean;
/** The data returned from the last successful mutation, or null */
data: TData | null;
/** The error from the last failed mutation, or null */
error: TError | null;
/** True after a successful mutation — distinct from data since data can be null on success */
isSuccess: boolean;
}
interface MutationResult<TData, TError extends Error = Error> {
/** The returned data on success, or null on failure */
data: TData | null;
/** The error on failure, or null on success */
error: TError | null;
}
interface Mutation<TData, TError extends Error = Error, TVariables = void> {
/**
* Execute the mutation. Always resolves — never throws.
* Returns `{ data, error }` so you can await it or fire and forget safely.
*
* @example
* // Fire and forget — safe
* createPost.mutate({ title: 'Hello' });
*
* // Await for local UI control — no try/catch needed
* const { data, error } = await createPost.mutate({ title: 'Hello' });
* if (!error) router.push('/posts');
*/
mutate: (...args: TVariables extends void ? [] : [variables: TVariables]) => Promise<MutationResult<TData, TError>>;
/** Returns the current mutation state */
get: () => MutationState<TData, TError>;
/** Subscribe to state changes. Returns an unsubscribe function. */
subscribe: (callback: (state: MutationState<TData, TError>) => void) => () => void;
/** Reset state back to initial — clears data, error, isSuccess */
reset: () => void;
}
/**
* Creates a reactive mutation for POST, PUT, DELETE, or any async side effect.
*
* Always returns a promise that resolves — never throws.
* On success, automatically reloads any chunks listed in `invalidates`.
*
* @param mutationFn - Async function that performs the side effect.
* @param options.invalidates - Chunks to reload after a successful mutation.
* @param options.onSuccess - Called with data and variables on success.
* @param options.onError - Called with error and variables on failure.
* @param options.onSettled - Called after every attempt regardless of outcome.
*
* @example
* const createPost = mutation(
* async (data: NewPost) => fetchAPI('/posts', { method: 'POST', body: data }),
* {
* invalidates: [postsChunk],
* onSuccess: (data) => toast.success('Post created!'),
* onError: (err) => toast.error(err.message),
* }
* );
*
* // Fire and forget
* createPost.mutate({ title: 'Hello' });
*
* // Await for local control
* const { data, error } = await createPost.mutate({ title: 'Hello' });
* if (!error) router.push('/posts');
*/
declare function mutation<TData, TError extends Error = Error, TVariables = void>(mutationFn: MutationFn<TData, TVariables>, options?: MutationOptions<TData, TError, TVariables>): Mutation<TData, TError, TVariables>;
type index_AsyncChunk<T, E extends Error = Error> = AsyncChunk<T, E>;
type index_AsyncState<T, E extends Error> = AsyncState<T, E>;
type index_AsyncStateWithPagination<T, E extends Error> = AsyncStateWithPagination<T, E>;
type index_GlobalQueryConfig = GlobalQueryConfig;
type index_Mutation<TData, TError extends Error = Error, TVariables = void> = Mutation<TData, TError, TVariables>;
type index_MutationFn<TData, TVariables> = MutationFn<TData, TVariables>;
type index_MutationOptions<TData, TError extends Error = Error, TVariables = void> = MutationOptions<TData, TError, TVariables>;
type index_MutationResult<TData, TError extends Error = Error> = MutationResult<TData, TError>;
type index_MutationState<TData, TError extends Error = Error> = MutationState<TData, TError>;
type index_PaginatedAsyncChunk<T, E extends Error = Error> = PaginatedAsyncChunk<T, E>;
type index_PaginatedParamAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}> = PaginatedParamAsyncChunk<T, E, P>;
declare const index_asyncChunk: typeof asyncChunk;
declare const index_combineAsyncChunks: typeof combineAsyncChunks;
declare const index_configureQuery: typeof configureQuery;
declare const index_getGlobalQueryConfig: typeof getGlobalQueryConfig;
declare const index_infiniteAsyncChunk: typeof infiniteAsyncChunk;
declare const index_mutation: typeof mutation;
declare const index_paginatedAsyncChunk: typeof paginatedAsyncChunk;
declare const index_resetQueryConfig: typeof resetQueryConfig;
declare namespace index {
export { type index_AsyncChunk as AsyncChunk, type index_AsyncState as AsyncState, type index_AsyncStateWithPagination as AsyncStateWithPagination, type index_GlobalQueryConfig as GlobalQueryConfig, type index_Mutation as Mutation, type index_MutationFn as MutationFn, type index_MutationOptions as MutationOptions, type index_MutationResult as MutationResult, type index_MutationState as MutationState, type index_PaginatedAsyncChunk as PaginatedAsyncChunk, type index_PaginatedParamAsyncChunk as PaginatedParamAsyncChunk, index_asyncChunk as asyncChunk, index_combineAsyncChunks as combineAsyncChunks, index_configureQuery as configureQuery, index_getGlobalQueryConfig as getGlobalQueryConfig, index_infiniteAsyncChunk as infiniteAsyncChunk, index_mutation as mutation, index_paginatedAsyncChunk as paginatedAsyncChunk, index_resetQueryConfig as resetQueryConfig };
}
export { type Chunk, type Middleware, batch, chunk, computed, getChunkMeta, isChunk, isValidChunkValue, index$1 as middleware, index as query, select };