@harlem/core
Version:
Powerfully simple global state management for Vue 3
407 lines (402 loc) • 16.3 kB
TypeScript
import * as _harlem_utilities from '@harlem/utilities';
import { Matcher, Matchable, Disposable, UnionToIntersection } from '@harlem/utilities';
import { Plugin, DeepReadonly, App, ComputedRef } from 'vue';
declare global {
interface Window {
$harlem: {
createInstance(): HarlemInstance;
};
}
}
type BaseState = Record<PropertyKey, any>;
type StoreProducer<TState extends BaseState> = keyof StoreProducers<TState>;
type ReadState<TState extends BaseState> = DeepReadonly<TState>;
type WriteState<TState extends BaseState> = TState;
type StoreRegistrations = Record<string, Map<string, StoreRegistration>>;
type RegistrationType = 'ref' | 'reactive' | 'computed' | 'other';
type RegistrationValueProducer = () => unknown;
type Getter<TState extends BaseState, TResult> = (state: ReadState<TState>) => TResult;
type Mutator<TState extends BaseState, TPayload, TResult = void> = (state: WriteState<TState>, payload: TPayload) => TResult;
type Mutation<TPayload, TResult = void> = undefined extends TPayload ? (payload?: TPayload) => TResult : (payload: TPayload) => TResult;
type ActionBody<TState extends BaseState, TPayload = undefined, TResult = void> = (payload: TPayload, mutator: (mutate: Mutator<TState, undefined, void>) => void) => Promise<TResult>;
type Action<TPayload, TResult = void> = undefined extends TPayload ? (payload?: TPayload) => Promise<TResult> : (payload: TPayload) => Promise<TResult>;
type EventHandler<TData = any> = (payload?: EventPayload<TData>) => void;
type Trigger = <TPayload = any, TResult = any>(matcher: Matcher | Matchable, handler: TriggerHandler<TPayload, TResult>) => Disposable;
type TriggerHandler<TPayload = any, TResult = any> = (data: TriggerEventData<TPayload, TResult>) => void;
type BranchAccessor<TState extends BaseState, TValue> = (state: ReadState<TState>) => TValue;
type InternalStores = Map<string, InternalStore<BaseState>>;
type HarlemPlugin = (app: App, eventBus: EventBus, stores: InternalStores) => void;
type Extension<TState extends BaseState> = (store: InternalStore<TState>) => Record<string, any>;
type ExtensionAPIs<TExtensions extends Extension<BaseState>[]> = Record<string, any> extends UnionToIntersection<ReturnType<TExtensions[number]>> ? unknown : UnionToIntersection<ReturnType<TExtensions[number]>>;
type PublicStore<TState extends BaseState, TExtensions extends Extension<TState>[]> = Omit<Store<TState>, keyof ExtensionAPIs<TExtensions>> & ExtensionAPIs<TExtensions>;
interface StoreRegistration {
type: RegistrationType;
producer: RegistrationValueProducer;
}
interface EventBus {
/**
* Subscribe to an event.
*
* @param event - The name of the event to subscribe to
* @param handler - A handler called when the event is fired
*/
on(event: string, handler: EventHandler): Disposable;
/**
* Subscribe to an event. Once the event is fired once, this listener is automatically detached.
*
* @param event - The name of the event to subscribe to
* @param handler - A handler called when the event is fired
*/
once(event: string, handler: EventHandler): Disposable;
/**
* Unsubscribe from an event.
*
* @param event - The name of the event to unsubscribe from
* @param handler - The handler the was registered to the event
*/
off(event: string, handler: EventHandler): void;
/**
* Publish an event.
*
* @param event - The name of the event to publish
* @param payload - An optional payload to publish with the event
*/
emit(event: string, payload?: EventPayload): void;
}
interface EventPayload<TData = any> {
/**
* The name of the event sender. This could be the core library, any registered extension/plugin, or user-emitted events.
*/
sender: string;
/**
* The store on which this event took place.
*/
store: string;
/**
* A payload sent along with the event.
*/
data: TData;
}
interface TriggerEventData<TPayload = any, TResult = any> {
/**
* The name of the mutation/action that fired this trigger.
*/
name: string;
/**
* The payload provided to the mutation/action that fired this trigger.
*/
payload: TPayload;
/**
* The result returned from the mutation/action that fired this trigger. This is only populated for after and success triggers.
*/
result?: TResult;
}
interface StoreSnapshot<TState extends BaseState> {
/**
* A readonly copy of state in the snapshot
*/
get state(): TState;
/**
* Apply the current snapshot's state to the store. This will essentially overwrite any changes to state since this snapshot was taken.
* @param branchAccessor - An optional branch accessor to apply a partial part of this snapshot to the store.
* @param mutationName - An optional mutation name to use when applying the snapshot. This is useful for identifying snapshot applications in the Harlem devtools.
*/
apply<TValue>(branchAccessor?: BranchAccessor<TState, TValue>, mutationName?: string): void;
}
interface StoreBase<TState extends BaseState> {
/**
* Register a getter on this store
*
* @param name - The name of this getter
* @param getter - A function returning the computed value from state
*/
getter<TResult>(name: string, getter: Getter<TState, TResult>): ComputedRef<TResult>;
/**
* Register a mutation on this store
*
* @param name - The name of this mutation
* @param mutator - A function used to mutate state. This function receives state and a payload as it's parameters.
*/
mutation<TPayload, TResult = void>(name: string, mutator: Mutator<TState, TPayload, TResult>): Mutation<TPayload, TResult>;
/**
* Register an action on this store
*
* @param name - The name of this action
* @param body - The function to execute as part of this action. This function receives a payload and mutator function as it's parameters.
*/
action<TPayload, TResult = void>(name: string, body: ActionBody<TState, TPayload, TResult>): Action<TPayload, TResult>;
/**
* Listen to an event on this store. This is useful for creating triggers.
*
* @param event - The name of the event to listen to
* @param handler - The handler that will be called when the event is triggered
*/
on(event: string, handler: EventHandler): Disposable;
/**
* Listen to an event on this store (only executed once)
*
* @param event - The name of the event to listen to
* @param handler - The handler that will be called when the event is triggered
*/
once(event: string, handler: EventHandler): Disposable;
/**
* Suppress events emitted from this store for the duration of the function callback
*
* @param callback - A function during which all events will be suppressed on this store
*/
suppress<TResult = void>(callback: () => TResult): TResult;
/**
* Take a snapshot of this store's current state
*/
snapshot(): StoreSnapshot<TState>;
/**
* Reset this store back to it's intial state
*
* @param branchAccessor - An optional function that returns a sub-branch of state to reset
*/
reset<TValue>(branchAccessor?: BranchAccessor<TState, TValue>): void;
/**
* Destroy this store
*/
destroy(): void;
}
interface StoreProducers<TState extends BaseState> {
/**
* The provider used when exposing writable state
*
* @param state - The writable state object
*/
read(state: ReadState<TState>): ReadState<TState>;
/**
* The provider used when exposing writable state
*
* @param state - The writable state object
*/
write(state: WriteState<TState>): WriteState<TState>;
/**
* The provider used when exposing payloads to mutators
*
* @param payload - The payload supplied to the mutation (or requester)
*/
payload<TPayload>(payload: TPayload): TPayload;
}
interface InternalStore<TState extends BaseState = BaseState> extends StoreBase<TState> {
/**
* The name of this store
*/
readonly name: string;
/**
* A boolean indicating whether this store allows overwriting duplicate registrations
*/
readonly allowsOverwrite: boolean;
/**
* The current (readonly) state object
*/
readonly state: ReadState<TState>;
/**
* Flags defined on this store
*/
readonly flags: Map<string, unknown>;
/**
* The producers for this store
*/
readonly producers: StoreProducers<TState>;
/**
* The items registered with this store
*/
readonly registrations: StoreRegistrations;
/**
* Checks whether an item with the specified name is registered under the specified group on this store
*
* @param group - The group this item is registered under
* @param name - The name of the registration
*/
hasRegistration(group: string, name: string): boolean;
/**
* Gets a registered item with the specified name
*
* @param group - The group this item is registered under
* @param name - The name of the registration
*/
getRegistration(group: string, name: string): StoreRegistration | undefined;
/**
* Register a new item on this store
*
* @param group - The group this item will be registered under
* @param name - The name of this registration
* @param valueProducer - A function returning the value that represents this registration
* @param type - The type of registration this is
*/
register(group: string, name: string, valueProducer: RegistrationValueProducer, type?: RegistrationType): void;
/**
* Remove a registration from this store
*
* @param group - The group this item is registered under
* @param name - The name of the registration
*/
unregister(group: string, name: string): void;
/**
* Emit an event from this store
*
* @param event - The name of the event to emit
* @param sender - The name of the sender
* @param data - Any data to be emitted with this event
*/
emit(event: string, sender: string, data: any): void;
/**
* Register reactive effects with this store to be disposed when the store is destroyed
*
* @param callback - A function during which reactive effects will be tracked
*/
track<TResult>(callback: () => TResult): TResult;
/**
* Perform a write operation on this store
*
* @param name - The name that will be used for this mutation operation
* @param sender - The sender of the mutation
* @param mutator - A function which will be used to mutate state
* @param suppress - A boolean indication whether to suppress events for this mutation
*/
write<TResult = void>(name: string, sender: string, mutator: Mutator<TState, undefined, TResult>, suppress?: boolean): TResult;
}
interface InternalStoreOptions<TState extends BaseState> {
/**
* A boolean indicating whether this store allows overwriting duplicate registrations
*/
allowsOverwrite: boolean;
/**
* A set of providers used by this store
*/
producers: Partial<StoreProducers<TState>>;
}
interface StoreOptions<TState extends BaseState, TExtensions extends Extension<TState>[]> extends InternalStoreOptions<TState> {
/**
* An optional array of extensions to extend this store with
*/
extensions?: TExtensions;
}
interface Store<TState extends BaseState> extends StoreBase<TState> {
/**
* The current (readonly) state object
*/
state: ReadState<TState>;
/**
* The trigger called before a mutation runs
*/
onBeforeMutation: Trigger;
/**
* The trigger called after a mutation runs, regardless of it was successful or not
*/
onAfterMutation: Trigger;
/**
* The trigger called upon successful completion of a mutation
*/
onMutationSuccess: Trigger;
/**
* The trigger called when a mutation fails
*/
onMutationError: Trigger;
/**
* The trigger called before an action runs
*/
onBeforeAction: Trigger;
/**
* The trigger called after an action runs, regardless of it was successful or not
*/
onAfterAction: Trigger;
/**
* The trigger called upon successful completion of an action
*/
onActionSuccess: Trigger;
/**
* The trigger called when an action fails
*/
onActionError: Trigger;
}
interface HarlemOptions {
/**
* An optional array of plugins to register with Harlem
*/
plugins?: HarlemPlugin[];
}
interface HarlemInstance extends Omit<EventBus, 'emit'> {
/**
* Attach Harlem to a Vue application. This is required for Harlem plugins to be usable.
*
* @param app - The Vue application instance to attach to
* @param options - Harlem options
*/
createVuePlugin(options?: HarlemOptions): Plugin;
/**
* Create a new Harlem store.
*
* @param name - The name of this store.
* @param state - The initial state of this store.
* @param options - Additional options used to configure this store.
*
* @example
* // Define the initial state of this store
* const STATE = {
* firstName: 'John',
* lastName: 'Smith'
* };
*
* // Create the store with the initial state and any options/extensions
* const {
* state,
* getter,
* mutation,
* action
* } = createStore('app', STATE, {
* extensions: [
* actionExtension()
* ]
* })
*/
createStore<TState extends BaseState, TExtensions extends Extension<TState>[]>(name: string, state: TState, options?: Partial<StoreOptions<TState, TExtensions>>): PublicStore<TState, TExtensions>;
}
declare const EVENTS: {
readonly core: {
readonly installed: "core:installed";
};
readonly store: {
readonly created: "store:created";
readonly ready: "store:ready";
readonly destroyed: "store:destroyed";
};
readonly mutation: {
readonly before: "mutation:before";
readonly after: "mutation:after";
readonly success: "mutation:success";
readonly error: "mutation:error";
};
readonly action: {
readonly before: "action:before";
readonly after: "action:after";
readonly success: "action:success";
readonly error: "action:error";
};
readonly ssr: {
readonly initServer: "ssr:init:server";
readonly initClient: "ssr:init:client";
};
readonly devtools: {
readonly update: "devtools:update";
readonly reset: "devtools:reset";
};
};
declare const PRODUCERS: StoreProducers<any>;
declare const INTERNAL: {
readonly prefix: "$harlem:";
readonly pattern: RegExp;
};
/**
* Create a new instance of Harlem. This is useful in multi-app scenarios.
*/
declare function createInstance(): HarlemInstance;
declare const on: (event: string, handler: EventHandler<any>) => _harlem_utilities.Disposable;
declare const off: (event: string, handler: EventHandler<any>) => void;
declare const once: (event: string, handler: EventHandler<any>) => _harlem_utilities.Disposable;
declare const createVuePlugin: (options?: HarlemOptions | undefined) => Plugin;
declare const createStore: <TState extends BaseState, TExtensions extends Extension<TState>[]>(name: string, state: TState, options?: Partial<StoreOptions<TState, TExtensions>> | undefined) => PublicStore<TState, TExtensions>;
export { Action, ActionBody, BaseState, BranchAccessor, EVENTS, EventBus, EventHandler, EventPayload, Extension, ExtensionAPIs, Getter, HarlemInstance, HarlemOptions, HarlemPlugin, INTERNAL, InternalStore, InternalStoreOptions, InternalStores, Mutation, Mutator, PRODUCERS, PublicStore, ReadState, RegistrationType, RegistrationValueProducer, Store, StoreBase, StoreOptions, StoreProducer, StoreProducers, StoreRegistration, StoreRegistrations, StoreSnapshot, Trigger, TriggerEventData, TriggerHandler, WriteState, createInstance, createStore, createVuePlugin, off, on, once };