Skip to content

Commit

Permalink
refactor(persist): update persist.ts (#50)
Browse files Browse the repository at this point in the history
Refactor persist.ts to improve code organization and readability.
- Add support for transform functions to modify the state before storing and after retrieving from storage.
- Implement inbound and outbound transformers to handle state transformations.
- Handle errors thrown by transformers and log them to the console.
- Use the transformed state when reconciling and persisting the store.
- Update persistStoreMdw function to use the transformed state when saving to storage.
- Add tests for persist transformers
  • Loading branch information
VldMrgnn authored Oct 3, 2024
1 parent eb28818 commit fbe29cb
Show file tree
Hide file tree
Showing 2 changed files with 851 additions and 10 deletions.
74 changes: 66 additions & 8 deletions store/persist.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Err, Ok, type Operation, type Result } from "../deps.ts";
import type { AnyState, Next } from "../types.ts";
import { Err, Ok, Operation, Result } from "../deps.ts";
import { select, updateStore } from "./fx.ts";
import type { UpdaterCtx } from "./types.ts";

import type { AnyState, Next } from "../types.ts";
import type { UpdaterCtx } from "./types.ts";
export const PERSIST_LOADER_ID = "@@starfx/persist";

export interface PersistAdapter<S extends AnyState> {
Expand All @@ -17,6 +17,35 @@ export interface PersistProps<S extends AnyState> {
key: string;
reconciler: (original: S, rehydrated: Partial<S>) => S;
rehydrate: () => Operation<Result<unknown>>;
transform?: TransformFunctions<S>;
}
interface TransformFunctions<S extends AnyState> {
in(s: Partial<S>): Partial<S>;
out(s: Partial<S>): Partial<S>;
}

export function createTransform<S extends AnyState>() {
const transformers: TransformFunctions<S> = {
in: function (currentState: Partial<S>): Partial<S> {
return currentState;
},
out: function (currentState: Partial<S>): Partial<S> {
return currentState;
},
};

const inTransformer = function (state: Partial<S>): Partial<S> {
return transformers.in(state);
};

const outTransformer = function (state: Partial<S>): Partial<S> {
return transformers.out(state);
};

return {
in: inTransformer,
out: outTransformer,
};
}

export function createLocalStorageAdapter<S extends AnyState>(): PersistAdapter<
Expand Down Expand Up @@ -51,7 +80,13 @@ export function shallowReconciler<S extends AnyState>(
}

export function createPersistor<S extends AnyState>(
{ adapter, key = "starfx", reconciler = shallowReconciler, allowlist = [] }:
{
adapter,
key = "starfx",
reconciler = shallowReconciler,
allowlist = [],
transform,
}:
& Pick<PersistProps<S>, "adapter">
& Partial<PersistProps<S>>,
): PersistProps<S> {
Expand All @@ -60,9 +95,18 @@ export function createPersistor<S extends AnyState>(
if (!persistedState.ok) {
return Err(persistedState.error);
}
let stateFromStorage = persistedState.value as Partial<S>;

if (transform) {
try {
stateFromStorage = transform.out(persistedState.value);
} catch (err: any) {
console.error("Persistor outbound transformer error:", err);
}
}

const state = yield* select((s) => s);
const nextState = reconciler(state as S, persistedState.value);
const nextState = reconciler(state as S, stateFromStorage);
yield* updateStore<S>(function (state) {
Object.keys(nextState).forEach((key: keyof S) => {
state[key] = nextState[key];
Expand All @@ -78,25 +122,39 @@ export function createPersistor<S extends AnyState>(
allowlist,
reconciler,
rehydrate,
transform,
};
}

export function persistStoreMdw<S extends AnyState>(
{ allowlist, adapter, key }: PersistProps<S>,
{ allowlist, adapter, key, transform }: PersistProps<S>,
) {
return function* (_: UpdaterCtx<S>, next: Next) {
yield* next();
const state = yield* select((s: S) => s);

let transformedState: Partial<S> = state;
if (transform) {
try {
transformedState = transform.in(state);
} catch (err: any) {
console.error("Persistor inbound transformer error:", err);
}
}

// empty allowlist list means save entire state
if (allowlist.length === 0) {
yield* adapter.setItem(key, state);
yield* adapter.setItem(key, transformedState);
return;
}

const allowedState = allowlist.reduce<Partial<S>>((acc, key) => {
acc[key] = state[key];
if (key in transformedState) {
acc[key] = transformedState[key] as S[keyof S];
}
return acc;
}, {});

yield* adapter.setItem(key, allowedState);
};
}
Loading

0 comments on commit fbe29cb

Please sign in to comment.