UNPKG

@larscom/ngrx-signals-storage

Version:

Save signal state (@ngrx/signals) to localstorage/sessionstorage and restore the state on page load (with SSR support).

1 lines 7.73 kB
{"version":3,"file":"larscom-ngrx-signals-storage.mjs","sources":["../../../projects/ngrx-signals-storage/src/lib/ssr.ts","../../../projects/ngrx-signals-storage/src/lib/config.ts","../../../projects/ngrx-signals-storage/src/lib/with-storage.ts","../../../projects/ngrx-signals-storage/src/larscom-ngrx-signals-storage.ts"],"sourcesContent":["export class NoopStorage implements Storage {\n length = -1\n clear = () => {}\n getItem = (_key: string) => null\n key = (_index: number) => null\n removeItem = (_key: string) => {}\n setItem = (_key: string, _value: string) => {}\n}\n\nfunction isBrowser() {\n return typeof window !== 'undefined'\n}\n\n/**\n * Helper function for SSR (Server Side Rendered) apps to get a storage location.\n *\n * @example\n * export const CounterStore = signalStore(\n * withState({\n * count: 0\n * }),\n * withStorage('myKey', getStorage('sessionStorage'))\n * )\n *\n * Check out github for more information.\n * @see https://github.com/larscom/ngrx-signals-storage\n */\nexport function getStorage(storageType: 'localStorage' | 'sessionStorage'): Storage {\n switch (storageType) {\n case 'localStorage': {\n return isBrowser() ? window.localStorage : new NoopStorage()\n }\n case 'sessionStorage': {\n return isBrowser() ? window.sessionStorage : new NoopStorage()\n }\n default: {\n return new NoopStorage()\n }\n }\n}\n","export interface Config<T> {\n /**\n * These keys will not get saved to storage\n */\n excludeKeys: Array<keyof T>\n\n /**\n * Serializer for the state, by default it uses `JSON.stringify()`\n * @param state the last state known before it gets saved to storage\n */\n serialize: (state: T) => string\n\n /**\n * Deserializer for the state, by default it uses `JSON.parse()`\n * @param state the last state known from the storage location\n */\n deserialize: (state: string) => T\n\n /**\n * Save to storage will only occur when this function returns true\n * @param state the last state known before it gets saved to storage\n */\n saveIf: (state: T) => boolean\n\n /**\n * Function that gets executed on a storage error (get/set)\n * @param error the error that occurred\n */\n error: (error: any) => void\n}\n\nexport const defaultConfig: Config<any> = {\n excludeKeys: [],\n\n serialize: (state: any) => JSON.stringify(state),\n\n deserialize: (state: string) => JSON.parse(state),\n\n saveIf: (state: any) => true,\n\n error: (error: any) => console.error(error)\n}\n","import { effect } from '@angular/core'\nimport { EmptyFeatureResult, SignalStoreFeature, SignalStoreFeatureResult, getState, patchState } from '@ngrx/signals'\nimport { Config, defaultConfig } from './config'\n\n/**\n * The `withStorage` function that lets you save the state to localstorage/sessionstorage\n * and rehydrate the state upon page load.\n *\n * @param key the key under which the state should be saved into `Storage`\n * @param storage an implementation of the `Storage` interface, like: `sessionStorage` or `localStorage`\n *\n * @example\n * // for apps *without* SSR (Server Side Rendering)\n * export const CounterStore = signalStore(\n * withState({\n * count: 0\n * }),\n * withStorage('myKey', sessionStorage)\n * )\n *\n * @example\n * // for apps *with* SSR (Server Side Rendering)\n * export const CounterStore = signalStore(\n * withState({\n * count: 0\n * }),\n * withStorage('myKey', getStorage('sessionStorage'))\n * )\n *\n * Check out github for more information.\n * @see https://github.com/larscom/ngrx-signals-storage\n */\nexport function withStorage<State extends SignalStoreFeatureResult>(\n key: string,\n storage: Storage,\n config?: Partial<Config<State['state']>>\n): SignalStoreFeature<State, EmptyFeatureResult> {\n const cfg = { ...defaultConfig, ...config }\n\n const item = getFromStorage(key, storage, cfg)\n const storageState: State['state'] | null = item ? cfg.deserialize(item) : null\n\n let hydrated = false\n\n return (store) => {\n if (storageState != null && !hydrated) {\n const stateSignals = store['stateSignals']\n const stateSignalKeys = Object.keys(stateSignals)\n const state = stateSignalKeys.reduce((state, key) => {\n const value = storageState[key as keyof State['state']]\n return value\n ? {\n ...state,\n [key]: value\n }\n : state\n }, getState(store))\n\n patchState(store, state)\n hydrated = true\n }\n\n effect(() => {\n const state = structuredClone(getState(store))\n try {\n if (cfg.saveIf(state)) {\n cfg.excludeKeys.forEach((key) => {\n delete state[key as keyof State['state']]\n })\n storage.setItem(key, cfg.serialize(state))\n }\n } catch (e) {\n cfg.error(e)\n }\n })\n\n return store\n }\n}\n\nfunction getFromStorage<State extends SignalStoreFeatureResult>(\n key: string,\n storage: Storage,\n cfg: Config<State>\n): string | null {\n try {\n return storage.getItem(key)\n } catch (e) {\n cfg.error(e)\n return null\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}