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, Svelte, and Vanilla JS/TS applications.
176 lines (164 loc) • 6.77 kB
text/typescript
type Subscriber<T> = (newValue: T) => void;
type Middleware<T> = (value: T, next: (newValue: T) => void) => void;
type NamedMiddleware<T> = {
name?: string;
fn: Middleware<T>;
};
interface Chunk<T> {
/** Get the current value of the chunk. */
get: () => 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) => Chunk<D>;
/** Reset the chunk to its initial value. */
reset: () => void;
/** Destroy the chunk and all its subscribers. */
destroy: () => void;
}
/**
* Batch multiple chunk updates into a single re-render.
* Useful for updating multiple chunks at once without causing multiple re-renders.
*/
declare function batch(callback: () => void): void;
declare function chunk<T>(initialValue: T, middleware?: (Middleware<T> | NamedMiddleware<T>)[]): Chunk<T>;
type AsyncChunkOpt<T, E extends Error> = {
initialData?: T | null;
onError?: (error: E) => void;
retryCount?: number;
retryDelay?: number;
};
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>;
};
interface AsyncState<T, E extends Error> {
loading: boolean;
error: E | null;
data: T | null;
lastFetched?: number;
}
interface RefreshConfig {
/** Time in milliseconds after which data becomes stale */
staleTime?: number;
/** Time in milliseconds to cache data before considering it expired */
cacheTime?: number;
/** Auto-refresh interval in milliseconds */
refetchInterval?: number;
}
interface AsyncChunkOptExtended<T, E extends Error> extends AsyncChunkOpt<T, E> {
refresh?: RefreshConfig;
/** Enable/disable the fetcher - useful for conditional fetching */
enabled?: boolean;
}
interface AsyncChunk<T, E extends Error = Error> extends Chunk<AsyncState<T, E>> {
/** Reload the data from the source. */
reload: () => Promise<void>;
/** Smart refresh - respects stale time */
refresh: () => Promise<void>;
/** Mutate the data directly. */
mutate: (mutator: (currentData: T | null) => T) => void;
/** Reset the state to the initial value. */
reset: () => void;
/** Clean up intervals and timeouts */
cleanup: () => void;
}
declare function asyncChunk<T, E extends Error = Error>(fetcher: () => Promise<T>, options?: AsyncChunkOptExtended<T, E>): AsyncChunk<T, E>;
declare function asyncChunk<T, E extends Error = Error, P extends any[] = []>(fetcher: (...params: P) => Promise<T>, options?: AsyncChunkOptExtended<T, E>): AsyncChunk<T, E> & {
/** Reload with new parameters */
reload: (...params: P) => Promise<void>;
/** Smart refresh with parameters */
refresh: (...params: P) => Promise<void>;
/** Set parameters for future calls */
setParams: (...params: P) => void;
};
type ChunkValue<T> = T extends Chunk<infer U> ? U : never;
type DependencyValues<T extends Chunk<any>[]> = {
[K in keyof T]: T[K] extends Chunk<any> ? ChunkValue<T[K]> : never;
};
interface Computed<T> extends Chunk<T> {
/**
* Checks if the computed value needs to be recalculated due to dependency changes.
* @returns True if the computed value is dirty, false otherwise.
*/
isDirty: () => boolean;
/** Manually forces recalculation of the computed value from its dependencies. */
recompute: () => void;
}
declare function computed<TDeps extends Chunk<any>[], TResult>(dependencies: [...TDeps], computeFn: (...args: DependencyValues<TDeps>) => TResult): Computed<TResult>;
interface SelectOptions {
/**
* Configuration options for selector functions.
* @property {boolean} [useShallowEqual] - When true, performs a shallow equality check
* on the derived selector results to prevent unnecessary updates.
*/
useShallowEqual?: boolean;
}
/**
* Creates a derived read-only chunk based on a selector function.
* @param sourceChunk The source chunk to derive from.
* @param selector A function that extracts part of the source value.
* @param options Optional settings for shallow equality comparison.
* @returns A read-only derived chunk.
*/
declare function select<T, S>(sourceChunk: Chunk<T>, selector: (value: T) => S, options?: SelectOptions): Chunk<S>;
declare function isValidChunkValue(value: unknown): boolean;
declare function isChunk<T>(value: unknown): value is Chunk<T>;
declare function once<T>(fn: () => T): () => T;
declare function combineAsyncChunks<T extends Record<string, AsyncChunk<any>>>(chunks: T): Chunk<CombinedState<T>>;
declare const logger: Middleware<any>;
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;
}
declare function withHistory<T>(baseChunk: Chunk<T>, options?: {
maxHistory?: number;
}): ChunkWithHistory<T>;
interface PersistOptions<T> {
key: string;
storage?: Storage;
serialize?: (value: T) => string;
deserialize?: (value: string) => T;
}
declare function withPersistence<T>(baseChunk: Chunk<T>, options: PersistOptions<T>): Chunk<T>;
declare const index_logger: typeof logger;
declare const index_nonNegativeValidator: typeof nonNegativeValidator;
declare const index_withHistory: typeof withHistory;
declare const index_withPersistence: typeof withPersistence;
declare namespace index {
export { index_logger as logger, index_nonNegativeValidator as nonNegativeValidator, index_withHistory as withHistory, index_withPersistence as withPersistence };
}
export { type Chunk, type Middleware, asyncChunk, batch, chunk, combineAsyncChunks, computed, isChunk, isValidChunkValue, index as middleware, once, select };