@chromahq/store
Version:
Centralized, persistent store for Chrome extensions using zustand, accessible from service workers and React, with chrome.storage.local persistence.
139 lines (130 loc) • 6.07 kB
TypeScript
import { StoreApi, StateCreator } from 'zustand';
import * as React from 'react';
import { ReactNode } from 'react';
import { StateCreator as StateCreator$1 } from 'zustand/vanilla';
type PersistOptions = {
name: string;
version?: number;
migrate?: (state: any, version: number) => any;
};
interface StoreDefinition {
name: string;
slices?: StateCreator<any, [], [], any>[];
persistence?: PersistOptions;
config?: Record<string, any>;
}
type ExtractSliceState<T> = T extends StateCreator<infer State, any, any, any> ? State : never;
type SliceCreator<T> = StateCreator<T, [], [], T>;
interface StoreConfig<T> {
slices: readonly StateCreator<any, [], [], any>[];
persist?: PersistOptions;
}
type MergeSlices<Slices extends readonly StateCreator<any, [], [], any>[]> = Slices extends readonly [infer First, ...infer Rest] ? First extends StateCreator<any, [], [], infer FirstState> ? Rest extends readonly StateCreator<any, [], [], any>[] ? FirstState & MergeSlices<Rest> : FirstState : {} : {};
interface CentralStore<T> extends StoreApi<T> {
}
declare function chromeStoragePersist<S>(options: PersistOptions): (config: StateCreator<S>) => StateCreator<S>;
declare function useCentralStore<T, U = T>(store: CentralStore<T>, selector: (state: T) => U): U;
declare function useCentralDispatch<T>(store: CentralStore<T>): {
(partial: T | Partial<T> | ((state: T) => T | Partial<T>), replace?: false): void;
(state: T | ((state: T) => T), replace: true): void;
};
interface Bridge {
send: <Req = unknown, Res = unknown>(key: string, payload?: Req) => Promise<Res>;
isConnected: boolean;
}
interface BridgeWithEvents extends Bridge {
on?: (key: string, handler: (payload: any) => void) => void;
}
interface BridgeWithHandlers extends Bridge {
register: (key: string, handler: (payload?: any) => any) => void;
broadcast: (key: string, payload: any) => void;
on?: (key: string, handler: (payload: any) => void) => void;
}
declare class BridgeStore<T> implements CentralStore<T> {
private bridge;
private listeners;
private currentState;
private previousState;
private storeName;
private ready;
constructor(bridge: BridgeWithEvents, initialState?: T, storeName?: string);
initialize: () => Promise<void>;
private setupStateSync;
private notifyListeners;
getState: () => T;
setState(partial: T | Partial<T> | ((state: T) => T | Partial<T>), replace?: false): void;
setState(state: T | ((state: T) => T), replace: true): void;
subscribe: (listener: (state: T, prevState: T) => void) => (() => void);
destroy: () => void;
getInitialState: () => T;
}
declare function createBridgeStore<T>(bridge: BridgeWithEvents, initialState?: T, storeName?: string): CentralStore<T>;
/**
* Generic action hook factory for any store instance.
* Usage:
* const store = createStore(mergeSlices(sliceA, sliceB));
* <StoreProvider store={store}> ... </StoreProvider>
* export const useWalletActions = createActionHookForStore(store, walletActions);
* export const useCounterActions = createActionHookForStore(store, counterActions);
* All hooks and actions share the same StoreProvider/context.
*/
declare function createActionHookForStore<S extends Record<string, any>, ActionMap extends Record<string, (...args: any[]) => void>>(store: CentralStore<S>, actionsFactory: (actions: ReturnType<typeof useStoreActions<S>>) => ActionMap): () => ActionMap;
/**
* Store actions helper: exposes update, updateWith, replace, setState
* Actions should be defined in your slice and accessed via useActions.
*/
declare function useStoreActions<T extends Record<string, any>>(store: CentralStore<T>): {
update: (partial: Partial<T>) => void;
updateWith: (updater: (state: T) => Partial<T>) => void;
replace: (newState: T) => void;
setState: {
(partial: T | Partial<T> | ((state: T) => T | Partial<T>), replace?: false): void;
(state: T | ((state: T) => T), replace: true): void;
};
};
declare function createStoreHooks<T extends Record<string, any>>(): {
createActionHook: <ActionMap extends Record<string, (...args: any[]) => void>>(actionsFactory: (actions: ReturnType<typeof useStoreActions<T>>) => ActionMap) => () => ActionMap;
StoreProvider: ({ store, children }: {
store: CentralStore<T>;
children: ReactNode;
}) => React.FunctionComponentElement<React.ProviderProps<CentralStore<T> | null>>;
useStore: <U>(selector: (state: T) => U) => U;
useStoreInstance: () => CentralStore<T>;
useActions: () => {
update: (partial: Partial<T>) => void;
updateWith: (updater: (state: T) => Partial<T>) => void;
replace: (newState: T) => void;
setState: {
(partial: T | Partial<T> | ((state: T) => T | Partial<T>), replace?: false): void;
(state: T | ((state: T) => T), replace: true): void;
};
};
useAction: <K extends { [K_1 in keyof T]: T[K_1] extends (...args: any[]) => any ? K_1 : never; }[keyof T]>(actionKey: K) => T[K];
};
/**
* Core store builder with fluent API
*/
declare class StoreBuilder<T = any> {
private config;
constructor(name?: string);
/**
* Add state slices to the store
*/
withSlices(...slices: StateCreator$1<any, [], [], any>[]): this;
/**
* Attach a bridge for cross-context communication
*/
withBridge(bridge?: BridgeWithEvents): this;
/**
* Create the store
*/
create(): Promise<CentralStore<T>>;
private createBaseStore;
private createServiceWorkerStore;
}
/**
* Create a new store builder
*/
declare function createStore<T = any>(name?: string): StoreBuilder<T>;
export { BridgeStore, StoreBuilder, chromeStoragePersist, createActionHookForStore, createBridgeStore, createStore, createStoreHooks, useCentralDispatch, useCentralStore };
export type { Bridge, BridgeWithEvents, BridgeWithHandlers, CentralStore, ExtractSliceState, MergeSlices, PersistOptions, SliceCreator, StoreConfig, StoreDefinition };