Skip to content

Commit

Permalink
feat(core): added snapshot and reset to store
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewcourtice committed Jun 26, 2022
1 parent a452eb4 commit 483b5f0
Show file tree
Hide file tree
Showing 5 changed files with 284 additions and 41 deletions.
5 changes: 5 additions & 0 deletions core/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ export const EVENTS = {
},
} as const;

export const MUTATIONS = {
snapshot: 'core:snapshot',
reset: 'core:reset',
} as const;

export const PROVIDERS = {
read: value => value,
write: value => value,
Expand Down
4 changes: 3 additions & 1 deletion core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function emitCreated(store: InternalStore, state: any): void {
eventEmitter.once(EVENTS.core.installed, created);
}

function getExtendedStore<TState extends BaseState, TExtensions extends Extension<TState>[]>(store: InternalStore, extensions: TExtensions): ReturnType<Extension<TState>> {
function getExtendedStore<TState extends BaseState, TExtensions extends Extension<TState>[]>(store: InternalStore<TState>, extensions: TExtensions): ReturnType<Extension<TState>> {
return extensions.reduce((output, extension) => {
let result = {};

Expand Down Expand Up @@ -185,6 +185,8 @@ export function createStore<TState extends BaseState, TExtensions extends Extens
getter: store.getter.bind(store),
mutation: store.mutation.bind(store),
action: store.action.bind(store),
snapshot: store.snapshot.bind(store),
reset: store.reset.bind(store),
suppress: store.suppress.bind(store),
on: store.on.bind(store),
once: store.once.bind(store),
Expand Down
46 changes: 45 additions & 1 deletion core/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import eventEmitter from './event-emitter';

import {
EVENTS,
INTERNAL,
MUTATIONS,
PROVIDERS,
SENDER,
} from './constants';
Expand All @@ -15,11 +17,18 @@ import {
readonly,
} from 'vue';

import {
clone,
identity,
overwrite,
} from '@harlem/utilities';

import type {
Action,
ActionBody,
ActionEventData,
BaseState,
BranchAccessor,
EventHandler,
EventListener,
EventPayload,
Expand All @@ -36,6 +45,7 @@ import type {
StoreProviders,
StoreRegistration,
StoreRegistrations,
StoreSnapshot,
WriteState,
} from './types';

Expand All @@ -47,7 +57,7 @@ function localiseHandler(name: string, handler: EventHandler): EventHandler {
};
}

export default class Store<TState extends BaseState = any> implements InternalStore<TState> {
export default class Store<TState extends BaseState = BaseState> implements InternalStore<TState> {

private options: InternalStoreOptions<TState>;
private flags: Map<string, unknown>;
Expand All @@ -56,6 +66,7 @@ export default class Store<TState extends BaseState = any> implements InternalSt
private isSuppressing: boolean;
private readState: ReadState<TState>;
private writeState: WriteState<TState>;
private initialState?: StoreSnapshot<TState>;

public name: string;
public registrations: StoreRegistrations;
Expand All @@ -79,6 +90,9 @@ export default class Store<TState extends BaseState = any> implements InternalSt
this.scope = effectScope();
this.writeState = reactive(state) as WriteState<TState>;
this.readState = readonly(this.writeState) as ReadState<TState>;

this.once(EVENTS.store.created, () => this.initialState = this.snapshot());
this.on(EVENTS.devtools.reset, () => this.reset());
}

public get allowsOverwrite(): boolean {
Expand Down Expand Up @@ -266,6 +280,36 @@ export default class Store<TState extends BaseState = any> implements InternalSt
return action;
}

public snapshot(): StoreSnapshot<TState> {
const snapshot = clone(this.state);

const apply = <TBranchState extends BaseState>(
branchAccessor: BranchAccessor<TState, TBranchState> = identity,
mutationName: string = MUTATIONS.snapshot) => {
this.write(mutationName, SENDER, state => {
if (!snapshot) {
return console.warn('Couldn\'t find snapshot for this operation!');
}

const source = branchAccessor(snapshot);
const target = branchAccessor(state);

overwrite(target, clone(source), INTERNAL.pattern);
});
};

return {
apply,
get state() {
return clone(snapshot);
},
};
}

public reset<TBranchState extends BaseState>(branchAccessor: BranchAccessor<TState, TBranchState> = identity) {
this.initialState?.apply(branchAccessor, MUTATIONS.reset);
}

public write<TResult = void>(name: string, sender: string, mutator: Mutator<TState, undefined, TResult>, suppress?: boolean): TResult {
const mutation = () => this.mutate(name, sender, mutator, undefined);

Expand Down
18 changes: 18 additions & 0 deletions core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export type Action<TPayload, TResult = void> = undefined extends TPayload ? (pay
export type EventHandler<TData = any> = (payload?: EventPayload<TData>) => void;
export type TriggerHandler<TEventData extends TriggerEventData> = (data: TEventData) => void;
export type Trigger<TEventData extends TriggerEventData> = (name: string | string[], handler: TriggerHandler<TEventData>) => EventListener;
export type BranchAccessor<TState extends BaseState, TBranchState extends BaseState> = (state: ReadState<TState> | WriteState<TState>) => TBranchState;
export type InternalStores = Map<string, InternalStore<any>>;
export type Extension<TState extends BaseState> = (store: InternalStore<TState>) => Record<string, any>;
export type ExtensionAPIs<TExtensions extends Extension<any>[]> = UnionToIntersection<ReturnType<TExtensions[number]>>;
Expand Down Expand Up @@ -63,6 +64,11 @@ export interface StoreRegistration {
producer: RegistrationValueProducer;
}

export interface StoreSnapshot<TState> {
get state(): TState;
apply<TBranchState extends BaseState>(branchCallback?: BranchAccessor<TState, TBranchState>, mutationName?: string): void;
}

export interface StoreBase<TState extends BaseState> {
/**
* Register a getter on this store
Expand Down Expand Up @@ -111,6 +117,18 @@ export interface StoreBase<TState extends BaseState> {
*/
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<TBranchState extends BaseState>(branchAccessor?: BranchAccessor<TState, TBranchState>): void;

/**
* Destroy this store
*/
Expand Down
Loading

0 comments on commit 483b5f0

Please sign in to comment.