{"id":13808971,"url":"https://github.com/larscom/ngrx-store-storagesync","last_synced_at":"2025-04-05T12:09:13.499Z","repository":{"id":34299803,"uuid":"175820344","full_name":"larscom/ngrx-store-storagesync","owner":"larscom","description":"Highly configurable state sync library between localStorage/sessionStorage and @ngrx/store (Angular)","archived":false,"fork":false,"pushed_at":"2025-03-27T07:55:50.000Z","size":5427,"stargazers_count":41,"open_issues_count":1,"forks_count":7,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-29T11:11:21.262Z","etag":null,"topics":["angular","localstorage","ngrx","redux","rxjs","sessionstorage","state","storage","store"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/larscom.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-03-15T12:56:50.000Z","updated_at":"2025-03-27T07:55:53.000Z","dependencies_parsed_at":"2023-12-24T12:31:22.934Z","dependency_job_id":"6dfdaed8-84c8-4f53-9a77-ae7d28142117","html_url":"https://github.com/larscom/ngrx-store-storagesync","commit_stats":{"total_commits":462,"total_committers":9,"mean_commits":"51.333333333333336","dds":"0.16233766233766234","last_synced_commit":"e6b71641c6917776535f3cf17c9a961e6841515d"},"previous_names":[],"tags_count":97,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/larscom%2Fngrx-store-storagesync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/larscom%2Fngrx-store-storagesync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/larscom%2Fngrx-store-storagesync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/larscom%2Fngrx-store-storagesync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/larscom","download_url":"https://codeload.github.com/larscom/ngrx-store-storagesync/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247332612,"owners_count":20921853,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","localstorage","ngrx","redux","rxjs","sessionstorage","state","storage","store"],"created_at":"2024-08-04T01:01:56.708Z","updated_at":"2025-04-05T12:09:13.475Z","avatar_url":"https://github.com/larscom.png","language":"TypeScript","funding_links":[],"categories":["State Management"],"sub_categories":["NgRx"],"readme":"# @larscom/ngrx-store-storagesync\n\n[![npm-version](https://img.shields.io/npm/v/@larscom/ngrx-store-storagesync.svg?label=npm)](https://www.npmjs.com/package/@larscom/ngrx-store-storagesync)\n![npm](https://img.shields.io/npm/dw/@larscom/ngrx-store-storagesync)\n[![license](https://img.shields.io/npm/l/@larscom/ngrx-store-storagesync.svg)](https://github.com/larscom/ngrx-store-storagesync/blob/main/LICENSE)\n\n\u003e **Highly configurable** state sync library between `localStorage/sessionStorage` and `@ngrx/store` (Angular)\n\n## Features\n\n- \u0026#10003; Sync with `localStorage` and `sessionStorage`\n- \u0026#10003; **Storage** option per feature state, for example:\n  - feature1 to `sessionStorage`\n  - feature2 to `localStorage`\n- \u0026#10003; Exclude **deeply** nested properties\n\n## Dependencies\n\n`@larscom/ngrx-store-storagesync` depends on [@ngrx/store](https://github.com/ngrx/store) and [Angular](https://github.com/angular/angular)\n\n## Installation\n\n```bash\nnpm install @larscom/ngrx-store-storagesync\n```\n\nChoose the version corresponding to your Angular version\n\n| @angular/core | @larscom/ngrx-store-storagesync |\n| ------------- | ------------------------------- |\n| \u003e= 12         | \u003e= 13.0.0                       |\n| \u003c 12          | \u003c= 6.3.0                        |\n\n## Usage\n\nInclude `storageSyncReducer` in your meta-reducers array in `StoreModule.forRoot`\n\n```ts\nimport { NgModule } from '@angular/core'\nimport { BrowserModule } from '@angular/platform-browser'\nimport { StoreModule } from '@ngrx/store'\nimport { routerReducer } from '@ngrx/router-store'\nimport { storageSync } from '@larscom/ngrx-store-storagesync'\nimport * as fromFeature1 from './feature/reducer'\n\nexport const reducers: ActionReducerMap\u003cIRootState\u003e = {\n  router: routerReducer,\n  feature1: fromFeature1.reducer\n}\n\nexport function storageSyncReducer(reducer: ActionReducer\u003cIRootState\u003e): ActionReducer\u003cIRootState\u003e {\n  // provide all feature states within the features array\n  // features which are not provided, do not get synced\n  const metaReducer = storageSync\u003cIRootState\u003e({\n    features: [\n      // save only router state to sessionStorage\n      { stateKey: 'router', storageForFeature: window.sessionStorage },\n\n      // exclude key 'success' inside 'auth' and all keys 'loading' inside 'feature1'\n      { stateKey: 'feature1', excludeKeys: ['auth.success', 'loading'] }\n    ],\n    // defaults to localStorage\n    storage: window.localStorage\n  })\n\n  return metaReducer(reducer)\n}\n\n// add storageSyncReducer to metaReducers\nconst metaReducers: MetaReducer\u003cany\u003e[] = [storageSyncReducer]\n\n@NgModule({\n  imports: [BrowserModule, StoreModule.forRoot(reducers, { metaReducers })]\n})\nexport class AppModule {}\n```\n\n## Configuration\n\n```ts\nexport interface IStorageSyncOptions\u003cT\u003e {\n  /**\n   * By default, states are not synced, provide the feature states you want to sync.\n   */\n  features: IFeatureOptions\u003cT\u003e[]\n  /**\n   * Provide the storage type to sync the state to, it can be any storage which implements the 'Storage' interface.\n   */\n  storage: Storage\n  /**\n   * Give the state a version. Version will be checked before rehydration.\n   *\n   * @examples\n   *  Version from Storage = 1 and Config.version = 2 --\u003e Skip hydration\n   *\n   *  Version from Storage = undefined and Config.version = 1 --\u003e Skip hydration\n   *\n   *  Version from Storage = 1 and Config.version = undefined --\u003e Skip hydration\n   *\n   *  Version from Storage = 1 and Config.version = 1 --\u003e Hydrate\n   */\n  version?: number\n  /**\n   * Under which key the version should be saved into storage\n   */\n  versionKey?: string\n  /**\n   * Function that gets executed on a storage error\n   * @param error the error that occurred\n   */\n  storageError?: (error: any) =\u003e void\n  /**\n   * Restore last known state from storage on startup\n   */\n  rehydrate?: boolean\n  /**\n   * Serializer for storage keys\n   * @param key the storage item key\n   */\n  storageKeySerializer?: (key: string) =\u003e string\n  /**\n   * Custom state merge function after rehydration (by default it does a deep merge)\n   * @param state the next state\n   * @param rehydratedState the state resolved from a storage location\n   */\n  rehydrateStateMerger?: (state: T, rehydratedState: T) =\u003e T\n}\n```\n\n```ts\nexport interface IFeatureOptions\u003cT\u003e {\n  /**\n   * The name of the feature state to sync\n   */\n  stateKey: string\n  /**\n   * Filter out (ignore) properties that exist on the feature state.\n   *\n   * @example\n   * // Filter/ignore key with name 'config' and name 'auth'\n   * ['config', 'auth']\n   *\n   * // Filter/ignore only key 'loading' inside object 'auth'\n   * ['auth.loading']\n   */\n  excludeKeys?: string[]\n  /**\n   * Provide the storage type to sync the feature state to,\n   * it can be any storage which implements the 'Storage' interface.\n   *\n   * It will override the storage property in StorageSyncOptions\n   * @see IStorageSyncOptions\n   */\n  storageForFeature?: Storage\n  /**\n   * Sync to storage will only occur when this function returns true\n   * @param featureState the next feature state\n   * @param state the next state\n   */\n  shouldSync?: (featureState: T[keyof T], state: T) =\u003e boolean\n  /**\n   * Serializer for storage keys (feature state),\n   * it will override the storageKeySerializer in StorageSyncOptions\n   * @see IStorageSyncOptions\n   *\n   * @param key the storage item key\n   */\n  storageKeySerializerForFeature?: (key: string) =\u003e string\n  /**\n   * Serializer for the feature state (before saving to a storage location)\n   * @param featureState the next feature state\n   */\n  serialize?: (featureState: T[keyof T]) =\u003e string\n  /**\n   * Deserializer for the feature state (after getting the state from a storage location)\n   *\n   * ISO Date objects which are stored as a string gets revived as Date object by default.\n   * @param featureState the feature state retrieved from a storage location\n   */\n  deserialize?: (featureState: string) =\u003e T[keyof T]\n}\n```\n\n## Examples\n\n### Sync to different storage locations\n\nYou can sync to different storage locations per feature state.\n\n```ts\nexport function storageSyncReducer(reducer: ActionReducer\u003cIRootState\u003e) {\n  return storageSync\u003cIRootState\u003e({\n    features: [\n      { stateKey: 'feature1', storageForFeature: window.sessionStorage }, // to sessionStorage\n      { stateKey: 'feature2' } // to localStorage because of 'default' which is set below.\n    ],\n    storage: window.localStorage // default\n  })(reducer)\n}\n```\n\n### Exclude specific properties on state\n\nPrevent specific properties from being synced to storage.\n\n```ts\nconst state: IRootState = {\n  feature1: {\n    message: 'hello', // excluded\n    loading: false,\n    auth: {\n      loading: false, // excluded\n      loggedIn: false,\n      message: 'hello' // excluded\n    }\n  }\n}\n\nexport function storageSyncReducer(reducer: ActionReducer\u003cIRootState\u003e) {\n  return storageSync\u003cIRootState\u003e({\n    features: [{ stateKey: 'feature1', excludeKeys: ['auth.loading', 'message'] }],\n    storage: window.localStorage\n  })(reducer)\n}\n```\n\n### Sync conditionally\n\nSync state to storage based on a condition.\n\n```ts\nconst state: IRootState = {\n  checkMe: true, // \u003c---\n  feature1: {\n    rememberMe: false, // \u003c---\n    auth: {\n      loading: false,\n      message: 'hello'\n    }\n  }\n}\n\nexport function storageSyncReducer(reducer: ActionReducer\u003cIRootState\u003e) {\n  return storageSync\u003cIRootState\u003e({\n    features: [\n      {\n        stateKey: 'feature1',\n        shouldSync: (feature1, state) =\u003e {\n          return feature1.rememberMe || state.checkMe\n        }\n      }\n    ],\n    storage: window.localStorage\n  })(reducer)\n}\n```\n\n### Serialize state\n\nOverride the default serializer for the feature state.\n\n```ts\nexport function storageSyncReducer(reducer: ActionReducer\u003cIRootState\u003e) {\n  return storageSync\u003cIRootState\u003e({\n    features: [\n      {\n        stateKey: 'feature1',\n        serialize: (feature1) =\u003e JSON.stringify(feature1)\n      }\n    ],\n    storage: window.localStorage\n  })(reducer)\n}\n```\n\n### Deserialize state\n\nOverride the default deserializer for the feature state.\n\n```ts\nexport function storageSyncReducer(reducer: ActionReducer\u003cIRootState\u003e) {\n  return storageSync\u003cIRootState\u003e({\n    features: [\n      {\n        stateKey: 'feature1',\n        deserialize: (feature1: string) =\u003e JSON.parse(feature1)\n      }\n    ],\n    storage: window.localStorage\n  })(reducer)\n}\n```\n\n### Serialize storage key\n\nOverride the default storage key serializer.\n\n```ts\nexport function storageSyncReducer(reducer: ActionReducer\u003cIRootState\u003e) {\n  return storageSync\u003cIRootState\u003e({\n    features: [{ stateKey: 'feature1' }],\n    storageKeySerializer: (key: string) =\u003e `abc_${key}`,\n    storage: window.localStorage\n  })(reducer)\n}\n```\n\n### Merge rehydrated state\n\nOverride the default rehydrated state merger.\n\n```ts\nexport function storageSyncReducer(reducer: ActionReducer\u003cIRootState\u003e) {\n  return storageSync\u003cIRootState\u003e({\n    features: [{ stateKey: 'feature1' }],\n    rehydrateStateMerger: (state: IRootState, rehydratedState: IRootState) =\u003e {\n      return { ...state, ...rehydratedState }\n    },\n    storage: window.localStorage\n  })(reducer)\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flarscom%2Fngrx-store-storagesync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flarscom%2Fngrx-store-storagesync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flarscom%2Fngrx-store-storagesync/lists"}