Full Code of rt2zz/redux-persist for AI

master d8b01a085e36 cached
51 files
91.1 KB
23.8k tokens
75 symbols
1 requests
Download .txt
Repository: rt2zz/redux-persist
Branch: master
Commit: d8b01a085e36
Files: 51
Total size: 91.1 KB

Directory structure:
gitextract_um4gqsx8/

├── .babelrc.js
├── .eslintrc.yml
├── .github/
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── .prettierrc
├── CHANGELOG.md
├── LIBSIZE.md
├── LICENSE
├── README.md
├── docs/
│   ├── MigrationGuide-v5.md
│   ├── PersistGate.md
│   ├── api.md
│   ├── hot-module-replacement.md
│   ├── migrations.md
│   └── v5-migration-alternate.md
├── integration/
│   ├── README.md
│   └── react/
│       └── package.json
├── package.json
├── rollup.config.js
├── scripts/
│   └── size-estimator.js
├── src/
│   ├── constants.ts
│   ├── createMigrate.ts
│   ├── createPersistoid.ts
│   ├── createTransform.ts
│   ├── getStoredState.ts
│   ├── index.ts
│   ├── integration/
│   │   ├── getStoredStateMigrateV4.ts
│   │   └── react.ts
│   ├── persistCombineReducers.ts
│   ├── persistReducer.ts
│   ├── persistStore.ts
│   ├── purgeStoredState.ts
│   ├── stateReconciler/
│   │   ├── autoMergeLevel1.ts
│   │   ├── autoMergeLevel2.ts
│   │   └── hardSet.ts
│   ├── storage/
│   │   ├── createWebStorage.ts
│   │   ├── getStorage.ts
│   │   ├── index.ts
│   │   └── session.ts
│   └── types.ts
├── tests/
│   ├── complete.spec.ts
│   ├── createPersistor.spec.ts
│   ├── flush.spec.ts
│   ├── persistCombineReducers.spec.ts
│   ├── persistReducer.spec.ts
│   ├── persistStore.spec.ts
│   └── utils/
│       ├── brokenStorage.ts
│       ├── createMemoryStorage.ts
│       ├── find.ts
│       └── sleep.ts
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .babelrc.js
================================================
/**
 * Babel Configuration
 */
 module.exports = {
  presets: [
    [
      "@babel/preset-env"
    ],
  ],
}


================================================
FILE: .eslintrc.yml
================================================
---

parser: '@typescript-eslint/parser'

globals:
  it: true
  expect: true
  describe: true
  test: true
  jest: true

plugins:
- '@typescript-eslint'

extends:
- eslint:recommended
- plugin:@typescript-eslint/recommended


================================================
FILE: .github/workflows/ci.yml
================================================
name: redux-persist ci
on: [push, pull_request]

jobs:
  # Label of the container job
  container-job:
    # Containers must run in Linux based operating systems
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [14.x, 16.x]
    
    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}
      - name: npm install
        run: |
          npm install
      - name: run test
        run: |
          npm run test


================================================
FILE: .gitignore
================================================
es
lib
dist
types
.watchmanconfig

# Logs
logs
*.log
npm-debug.log*

# Dependency directories
node_modules

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history

.DS_Store


================================================
FILE: .prettierrc
================================================
parser: "typescript"
semi: false
trailingComma: es5
singleQuote: true


================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project (after v6.1.0) should be documented in this file.

The format is (mostly) based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


## [6.1.0] - 2021-10-17
Thanks to [@smellman](https://github.com/smellman) for the TypeScript updates.

### Added
- TypeScript support
- GitHub Actions

### Changed
- Move from Flow to TypeScript
- Move from TravisCI to GitHub Actions ([.github/workflows/ci.yml](.github/workflows/ci.yml))
- Version updates for some dependencies

### Removed
- Flow
- TravisCI


================================================
FILE: LIBSIZE.md
================================================
### Redux Persist Size Estimate
The following is a history of size estimates in bytes. This is calculated as a rollup minified production build, excluding the size of redux which is an assumed peer dependency. YMMV.

**v5.6.7**: 4724 Bytes  
**v5.6.8**: 4724 Bytes  
**v5.6.9**: 4724 Bytes  
**v5.6.10**: 4724 Bytes  
**v5.6.11**: 4724 Bytes  
**v5.6.12**: 4724 Bytes  
**v5.7.0**: 4893 Bytes  
**v5.7.1**: 4894 Bytes  
**v5.7.2**: 4894 Bytes  
**v5.8.0**: 4894 Bytes  
**v5.9.0**: 4894 Bytes  
**v5.9.1**: 4894 Bytes  
**v5.10.0**: 4356 Bytes  
**v6.0.0-pre1**: 17783 Bytes  
**v6.0.0-pre2**: 11878 Bytes  
**v6.0.0-pre2.0**: 11934 Bytes  
**v6.0.0-pre2.1**: 5525 Bytes  
**v6.0.0**: 12167 Bytes  


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2017 Zack Story

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# Redux Persist
Persist and rehydrate a redux store.

[![build status](https://img.shields.io/travis/rt2zz/redux-persist/master.svg?style=flat-square)](https://travis-ci.org/rt2zz/redux-persist) [![npm version](https://img.shields.io/npm/v/redux-persist.svg?style=flat-square)](https://www.npmjs.com/package/redux-persist) [![npm downloads](https://img.shields.io/npm/dm/redux-persist.svg?style=flat-square)](https://www.npmjs.com/package/redux-persist)

## October 15th, 2021 - Move to TypeScript (Thanks [@smellman](https://github.com/smellman))

As part of the work to upgrade the infrastructure used to build redux-persist, we're moving from Flow to TypeScript.

- Move from Flow to TypeScript
- Move from TravisCI to GitHub Actions ([.github/workflows/ci.yml](.github/workflows/ci.yml))
- Version updates for some dependencies

## September 22nd, 2021 - Under New Management

Redux Persist is a staple project for Redux developers, both on mobile and on the web. If you're here, it's likely because you need it now or have used it before and need to debug something, and like me have possibly struggled with making it work (especially with newer versions of things) and making it work with _your_ code because the examples you'll find around the internet are inconsistent.

I ([@ckalika](https://github.com/ckalika)) spoke with [@rt2zz](https://github.com/rt2zz) about taking over maintenance of the project, and we agreed to give it a shot and see how we go. My priorities are as follows:

1. Go through and triage the existing issues
    - Separate them into bugs, feature requests, basic questions/requests for code samples, and issues that are either not project-specific or don't fall within the remit of the project (specific definitions and criteria will be posted in the future)
    - Determine the severity/urgency of each bug or feature request
    - Guestimate the size of them
    - Determine which are actionable immediately or in the short term
    - Establish some semblance of test criteria for each


2. Upgrade dependencies (where possible) so that we've got something building with modern versions
    * Note:  Right now, it's about modernising the project infrastructure and build process without making breaking API changes


3. Go through the existing pull requests
    - Merge the ones that deal with documentation, code samples, etc.
    - Review and merge the ones that deal with open issues
    - Review and merge the ones that will require breaking changes and consult authors about `redux-persist@v7` (feature set and requirements to be defined)


4. Update the documentation
    - Split it out for both web and mobile
    - Providing code samples and test coverage for how to use the library
    - Provide or link to working examples that integrate with additional libraries (e.g. [RTK Query](https://redux-toolkit.js.org/rtk-query/overview)).


5. Improve testing and automation
    - [x] Move to GitHub Actions
    - [ ] Move from Ava to Jest

There's a lot to do here, so I'll ask your patience and understanding as I work through it. If you have ideas for how to improve the library, the documentation, or the community, I'd love to hear them, and if you're submitting pull requests (or have submitted some previously), please reach out and help me understand what you're aiming to do with it.

I'll try to get some discussions up to pull together ideas, so we can properly work out what the next version is likely to look like.


## v6 upgrade
**Web**: no breaking changes
**React Native**: Users must now explicitly pass their storage engine in. e.g.
```js
import AsyncStorage from '@react-native-async-storage/async-storage';

const persistConfig = {
  //...
  storage: AsyncStorage
}
```

## Quickstart
`npm install redux-persist`

Usage Examples:
1. [Basic Usage](#basic-usage)
2. [Nested Persists](#nested-persists)
3. [Hot Module Replacement](./docs/hot-module-replacement.md)
4. Code Splitting [coming soon]

#### Basic Usage
Basic usage involves adding `persistReducer` and `persistStore` to your setup. **IMPORTANT** Every app needs to decide how many levels of state they want to "merge". The default is 1 level. Please read through the [state reconciler docs](#state-reconciler) for more information.

```js
// configureStore.js

import { createStore } from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage' // defaults to localStorage for web

import rootReducer from './reducers'

const persistConfig = {
  key: 'root',
  storage,
}

const persistedReducer = persistReducer(persistConfig, rootReducer)

export default () => {
  let store = createStore(persistedReducer)
  let persistor = persistStore(store)
  return { store, persistor }
}
```

If you are using react, wrap your root component with [PersistGate](./docs/PersistGate.md). This delays the rendering of your app's UI until your persisted state has been retrieved and saved to redux. **NOTE** the `PersistGate` loading prop can be null, or any react instance, e.g. `loading={<Loading />}`

```js
import { PersistGate } from 'redux-persist/integration/react'

// ... normal setup, create store and persistor, import components etc.

const App = () => {
  return (
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <RootComponent />
      </PersistGate>
    </Provider>
  );
};
```

## API
[Full API](./docs/api.md)

#### `persistReducer(config, reducer)`
  - arguments
    - [**config**](https://github.com/rt2zz/redux-persist/blob/master/src/types.js#L13-L27) *object*
      - required config: `key, storage`
      - notable other config: `whitelist, blacklist, version, stateReconciler, debug`
    - **reducer** *function*
      - any reducer will work, typically this would be the top level reducer returned by `combineReducers`
  - returns an enhanced reducer

#### `persistStore(store, [config, callback])`
  - arguments
    - **store** *redux store* The store to be persisted.
    - **config** *object* (typically null)
      - If you want to avoid that the persistence starts immediately after calling `persistStore`, set the option manualPersist. Example: `{ manualPersist: true }` Persistence can then be started at any point with `persistor.persist()`. You usually want to do this if your storage is not ready when the `persistStore` call is made.
    - **callback** *function* will be called after rehydration is finished.
  - returns **persistor** object

#### `persistor object`
  - the persistor object is returned by persistStore with the following methods:
    - `.purge()`
      - purges state from disk and returns a promise
    - `.flush()`
      - immediately writes all pending state to disk and returns a promise
    - `.pause()`
      - pauses persistence
    - `.persist()`
      - resumes persistence

## State Reconciler
State reconcilers define how incoming state is merged in with initial state. It is critical to choose the right state reconciler for your state. There are three options that ship out of the box, let's look at how each operates:

1. **hardSet** (`import hardSet from 'redux-persist/lib/stateReconciler/hardSet'`)
This will hard set incoming state. This can be desirable in some cases where persistReducer is nested deeper in your reducer tree, or if you do not rely on initialState in your reducer.
   - **incoming state**: `{ foo: incomingFoo }`
   - **initial state**: `{ foo: initialFoo, bar: initialBar }`
   - **reconciled state**: `{ foo: incomingFoo }` // note bar has been dropped
2. **autoMergeLevel1** (default)
This will auto merge one level deep. Auto merge means if the some piece of substate was modified by your reducer during the REHYDRATE action, it will skip this piece of state. Level 1 means it will shallow merge 1 level deep.
   - **incoming state**: `{ foo: incomingFoo }`
   - **initial state**: `{ foo: initialFoo, bar: initialBar }`
   - **reconciled state**: `{ foo: incomingFoo, bar: initialBar }` // note incomingFoo overwrites initialFoo
3. **autoMergeLevel2** (`import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2'`)
This acts just like autoMergeLevel1, except it shallow merges two levels
   - **incoming state**: `{ foo: incomingFoo }`
   - **initial state**: `{ foo: initialFoo, bar: initialBar }`
   - **reconciled state**: `{ foo: mergedFoo, bar: initialBar }` // note: initialFoo and incomingFoo are shallow merged

#### Example
```js
import hardSet from 'redux-persist/lib/stateReconciler/hardSet'

const persistConfig = {
  key: 'root',
  storage,
  stateReconciler: hardSet,
}
```

## React Integration
Redux persist ships with react integration as a convenience. The `PersistGate` component is the recommended way to delay rendering until persistence is complete. It works in one of two modes:
1. `loading` prop: The provided loading value will be rendered until persistence is complete at which point children will be rendered.
2. function children: The function will be invoked with a single `bootstrapped` argument. When bootstrapped is true, persistence is complete and it is safe to render the full app. This can be useful for adding transition animations.

## Blacklist & Whitelist
By Example:
```js
// BLACKLIST
const persistConfig = {
  key: 'root',
  storage: storage,
  blacklist: ['navigation'] // navigation will not be persisted
};

// WHITELIST
const persistConfig = {
  key: 'root',
  storage: storage,
  whitelist: ['navigation'] // only navigation will be persisted
};
```

## Nested Persists
Nested persist can be useful for including different storage adapters, code splitting, or deep filtering. For example while blacklist and whitelist only work one level deep, but we can use a nested persist to blacklist a deeper value:
```js
import { combineReducers } from 'redux'
import { persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'

import { authReducer, otherReducer } from './reducers'

const rootPersistConfig = {
  key: 'root',
  storage: storage,
  blacklist: ['auth']
}

const authPersistConfig = {
  key: 'auth',
  storage: storage,
  blacklist: ['somethingTemporary']
}

const rootReducer = combineReducers({
  auth: persistReducer(authPersistConfig, authReducer),
  other: otherReducer,
})

export default persistReducer(rootPersistConfig, rootReducer)
```

## Migrations
`persistReducer` has a general purpose "migrate" config which will be called after getting stored state but before actually reconciling with the reducer. It can be any function which takes state as an argument and returns a promise to return a new state object.

Redux Persist ships with `createMigrate`, which helps create a synchronous migration for moving from any version of stored state to the current state version. [[Additional information]](./docs/migrations.md)

## Transforms
Transforms allow you to customize the state object that gets persisted and rehydrated.

There are several libraries that tackle some common implementations for transforms.
- [immutable](https://github.com/rt2zz/redux-persist-transform-immutable) - support immutable reducers
- [seamless-immutable](https://github.com/hilkeheremans/redux-persist-seamless-immutable) - support seamless-immutable reducers
- [compress](https://github.com/rt2zz/redux-persist-transform-compress) - compress your serialized state with lz-string
- [encrypt](https://github.com/maxdeviant/redux-persist-transform-encrypt) - encrypt your serialized state with AES
- [filter](https://github.com/edy/redux-persist-transform-filter) - store or load a subset of your state
- [filter-immutable](https://github.com/actra-development/redux-persist-transform-filter-immutable) - store or load a subset of your state with support for immutablejs
- [expire](https://github.com/gabceb/redux-persist-transform-expire) - expire a specific subset of your state based on a property
- [expire-reducer](https://github.com/kamranahmedse/redux-persist-expire) - more flexible alternative to expire transformer above with more options

When the state object gets persisted, it first gets serialized with `JSON.stringify()`. If parts of your state object are not mappable to JSON objects, the serialization process may transform these parts of your state in unexpected ways. For example, the javascript [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) type does not exist in JSON. When you try to serialize a Set via `JSON.stringify()`, it gets converted to an empty object. Probably not what you want.

Below is a Transform that successfully persists a Set property, which simply converts it to an array and back. In this way, the Set gets converted to an Array, which is a recognized data structure in JSON. When pulled out of the persisted store, the array gets converted back to a Set before being saved to the redux store.

```js
import { createTransform } from 'redux-persist';

const SetTransform = createTransform(
  // transform state on its way to being serialized and persisted.
  (inboundState, key) => {
    // convert mySet to an Array.
    return { ...inboundState, mySet: [...inboundState.mySet] };
  },
  // transform state being rehydrated
  (outboundState, key) => {
    // convert mySet back to a Set.
    return { ...outboundState, mySet: new Set(outboundState.mySet) };
  },
  // define which reducers this transform gets called for.
  { whitelist: ['someReducer'] }
);

export default SetTransform;
```

The `createTransform` function takes three parameters.
1. An "inbound" function that gets called right before state is persisted (optional).
2. An "outbound" function that gets called right before state is rehydrated (optional).
3. A config object that determines which keys in your state will be transformed (by default no keys are transformed).

In order to take effect transforms need to be added to a `PersistReducer`’s config object.

```
import storage from 'redux-persist/lib/storage';
import { SetTransform } from './transforms';

const persistConfig = {
  key: 'root',
  storage: storage,
  transforms: [SetTransform]
};
```

## Storage Engines
- **localStorage** `import storage from 'redux-persist/lib/storage'`
- **sessionStorage** `import storageSession from 'redux-persist/lib/storage/session'`
- **[electron storage](https://github.com/psperber/redux-persist-electron-storage)** Electron support via [electron store](https://github.com/sindresorhus/electron-store)
- **[redux-persist-cookie-storage](https://github.com/abersager/redux-persist-cookie-storage)** Cookie storage engine, works in browser and Node.js, for universal / isomorphic apps
- **[redux-persist-expo-filesystem](https://github.com/t73liu/redux-persist-expo-filesystem)** react-native, similar to redux-persist-filesystem-storage but does not require linking or ejecting CRNA/Expo app. Only available if using Expo SDK (Expo, create-react-native-app, standalone).
- **[redux-persist-expo-securestore](https://github.com/Cretezy/redux-persist-expo-securestore)** react-native, for sensitive information using Expo's SecureStore. Only available if using Expo SDK (Expo, create-react-native-app, standalone).
- **[redux-persist-fs-storage](https://github.com/leethree/redux-persist-fs-storage)** react-native-fs engine
- **[redux-persist-filesystem-storage](https://github.com/robwalkerco/redux-persist-filesystem-storage)** react-native, to mitigate storage size limitations in android ([#199](https://github.com/rt2zz/redux-persist/issues/199), [#284](https://github.com/rt2zz/redux-persist/issues/284))
  **[redux-persist-indexeddb-storage](https://github.com/machester4/redux-persist-indexeddb-storage)** recommended for web via [localForage](https://github.com/localForage/localForage)
- **[redux-persist-node-storage](https://github.com/pellejacobs/redux-persist-node-storage)** for use in nodejs environments.
- **[redux-persist-pouchdb](https://github.com/yanick/redux-persist-pouchdb)** Storage engine for PouchDB.
- **[redux-persist-sensitive-storage](https://github.com/CodingZeal/redux-persist-sensitive-storage)** react-native, for sensitive information (uses [react-native-sensitive-info](https://github.com/mCodex/react-native-sensitive-info)).
- **[redux-persist-weapp-storage](https://github.com/cuijiemmx/redux-casa/tree/master/packages/redux-persist-weapp-storage)** Storage engine for wechat mini program, also compatible with wepy
- **[redux-persist-webextension-storage](https://github.com/ssorallen/redux-persist-webextension-storage)** Storage engine for browser (Chrome, Firefox) web extension storage
- **[@bankify/redux-persist-realm](https://github.com/bankifyio/redux-persist-realm)** Storage engine for Realm database, you will need to install Realm first
- **custom** any conforming storage api implementing the following methods: `setItem` `getItem` `removeItem`. (**NB**: These methods must support promises)

## Community & Contributing

I will be updating this section shortly. If you have a pull request that you've got outstanding, please reach out and I will try to review it and get it integrated. As we've shifted to TypeScript, that may necessitate some changes, but I'm happy to help in that regard, wherever I can.


================================================
FILE: docs/MigrationGuide-v5.md
================================================
## v5 Breaking Changes
There are three important breaking changes.
1. api has changed as described in the [migration](#migration-from-v4-to-v5) section below.
2. state with cycles is no longer serialized using `json-stringify-safe`, and will instead noop.
3. state methods can no longer be overridden which means all top level state needs to be plain objects. `redux-persist-transform-immutable` will continue to operate as before as it works on substate, not top level state.

Additionally v5 does not yet have typescript bindings.

## Migration from v4 to v5
**WARNING** v4 stored state is not compatible with v5. If you upgrade a v4 application, your users will lose their stored state upon upgrade. You can try the (highly) experimental [v4 -> v5 state migration](#experimental-v4-to-v5-state-migration) if you please. Feedback appreciated.

Standard Usage:
- remove **autoRehydrate**
- changes to **persistStore**:
  - 1. remove config argument (or replace with an null if you are using a callback)
  - 2. remove all arguments from the callback. If you need state you can call `store.getState()`
  - 3. all constants (ex: `REHYDRATE`, `PURGE`) has moved from `redux-persist/constants` to the root module.
- replace `combineReducers` with **persistCombineReducers**
  - e.g. `let reducer = persistCombineReducers(config, reducers)`
- changes to **config**:
  - `key` is now required. Can be set to anything, e.g. 'primary'
  - `storage` is now required. For default storage: `import storage from 'redux-persist/lib/storage'`

```diff
-import { REHYDRATE, PURGE } from 'redux-persist/constants'
-import { combineReducers } from 'redux'
+import { REHYDRATE, PURGE, persistCombineReducers } from 'redux-persist'
+import storage from 'redux-persist/lib/storage' // or whatever storage you are using

 const config = {
+  key: 'primary',
+  storage
 }

-let reducer = combineReducers(reducers)
+let reducer = persistCombineReducers(config, reducers)

 const store = createStore(
   reducer,
   undefined,
   compose(
     applyMiddleware(...),
-    autoRehydrate()
   )
 )

 const callback = ()

 persistStore(
   store,
-  config,
+  null,
   (
-     err, restoredState
   ) => {
+     store.getState() // if you want to get restoredState
   }
 )
```

Recommended Additions
- use new **PersistGate** to delay rendering until rehydration is complete
  - `import { PersistGate } from 'redux-persist/lib/integration/react'`
- set `config.debug = true` to get useful logging

If your implementation uses getStoredState + createPersistor see [alternate migration](./v5-migration-alternate.md)

## Why v5
Long story short, the changes are required in order to support new use cases
- code splitting reducers
- easier to ship persist support inside of other libs (e.g. redux-offline)
- ability to colocate persistence rules with the reducer it pertains to
- first class migration support
- enable PersistGate react component which blocks rendering until persistence is complete (and enables similar patterns for integration)
- possible to nest persistence
- guarantee consistent state atoms
- better debugability and extensibility

## Experimental v4 to v5 State Migration
- **warning: this method is completely untested**
- v5 getStoredState is not compatible with v4, so by default v5 will cause all of the persisted state from v4 to disappear on first run
- v5 ships with an experimental v4 -> v5 migration that works by overriding the default getStoredState implementation
**Warning** this is completely untested, please try and report back with any issues.
```js
import getStoredStateMigrateV4 from 'redux-persist/lib/integration/getStoredStateMigrateV4'
// ...
persistReducer({
  // ...
  getStoredState: getStoredStateMigrateV4(yourOldV4Config)
}, baseReducer)
```


================================================
FILE: docs/PersistGate.md
================================================
`PersistGate` delays the rendering of your app's UI until your persisted state has been retrieved and saved to redux.

**NOTE**: the `loading` prop can be `null` or any react instance to show during loading (e.g. a splash screen), for example `loading={<Loading />}`.

Example usage:

```js
import { PersistGate } from 'redux-persist/es/integration/react'

import configureStore from './store/configureStore'

const { persistor, store } = configureStore()

const onBeforeLift = () => {
  // take some action before the gate lifts
}

export default () => (
  <Provider store={store}>
    <PersistGate 
      loading={<Loading />}
      onBeforeLift={onBeforeLift}
      persistor={persistor}>
      <App />
    </PersistGate>
  </Provider>
)
```


================================================
FILE: docs/api.md
================================================
# Redux Persist API
---
## Standard API
- [persistReducer](#persistreducerconfig-reducer)([config](#type-persistconfig), reducer)
- [persistStore](#persiststorestore-config-callback)(store)
- [createMigrate](#createmigratemigrations-config)([migrations](#type-migrationmanifest))
### `persistReducer(config, reducer)`

```js
persistReducer(
  config: PersistConfig,
  reducer: Reducer,
): Reducer
```

Where Reducer is any reducer `(state, action) => state` and PersistConfig is [defined below](#type-persistconfig)

### `persistStore(store, config, callback)`
```js
persistStore(
  store: Store,
  config?: { enhancer?: Function },
  callback?: () => {}
): Persistor
```

Where Persistor is [defined below](#type-persistor)

### `createMigrate(migrations, config)`
```js
createMigrate(
  migrations: MigrationManifest,
  config?: { debug: boolean }
)
```

### `type Persistor`
```js
{
  purge: () => Promise<void>,
  flush: () => Promise<void>,
}
```

The Persistor is a redux store unto itself, plus
1. the `purge()` method for clearing out stored state.
2. the `flush()` method for flushing all pending state serialization and immediately write to disk

`purge()` method only clear the content of the storage, leaving the internal data of `redux` untouched. To clean it instead, you can use the [redux-reset](https://github.com/wwayne/redux-reset) module.

### `type PersistConfig`
```js
{
  key: string, // the key for the persist
  storage: Object, // the storage adapter, following the AsyncStorage api
  version?: number, // the state version as an integer (defaults to -1)
  blacklist?: Array<string>, // do not persist these keys
  whitelist?: Array<string>, // only persist these keys
  migrate?: (Object, number) => Promise<Object>,
  transforms?: Array<Transform>,
  throttle?: number, // ms to throttle state writes
  keyPrefix?: string, // will be prefixed to the storage key
  debug?: boolean, // true -> verbose logs
  stateReconciler?: false | StateReconciler, // false -> do not automatically reconcile state
  serialize?: boolean, // false -> do not call JSON.parse & stringify when setting & getting from storage
  writeFailHandler?: Function, // will be called if the storage engine fails during setItem()
}
```

Persisting state involves calling setItem() on the storage engine. By default, this will fail silently if the storage/quota is exhausted.  
Provide a writeFailHandler(error) function to be notified if this occurs.

### `type MigrationManifest`
```js
{
  [number]: (State) => State
}
```
Where the keys are state version numbers and the values are migration functions to modify state.

---
## Expanded API
The following methods are used internally by the standard api. They can be accessed directly if more control is needed.
### `getStoredState(config)`
```js
getStoredState(
  config: PersistConfig
): Promise<State>
```

Returns a promise (if Promise global is defined) of restored state.

### `createPersistoid(config)`
```js
createPersistoid(
  config
): Persistoid
```
Where Persistoid is [defined below](#type-persistoid).

### `type Persistoid`
```js
{
  update: (State) => void
}
```

### `type PersistorConfig`
```js
{
  enhancer: Function
}
```
Where enhancer will be sent verbatim to the redux createStore call used to create the persistor store. This can be useful for example to enable redux devtools on the persistor store.

### `type StateReconciler`
```js
(
  inboundState: State,
  originalState: State,
  reducedState: State,
) => State
```
A function which reconciles:
- **inboundState**: the state being rehydrated from storage
- **originalState**: the state before the REHYDRATE action
- **reducedState**: the store state *after* the REHYDRATE action but *before* the reconcilliation
into final "rehydrated" state.


================================================
FILE: docs/hot-module-replacement.md
================================================
## Hot Module Replacement

Hot Module Replacement (HMR) is a wonderful feature that is really useful in development environment. This allows you to update the code of your application without reloading the app and resetting the redux state.

The key modification for using HMR with redux-persist, is the incoming hot reducer needs to be re-persisted via `persistReducer`.

**configureStore.js**
```js
import { persistReducer } from 'redux-persist'
import rootReducer from './path/to/reducer'

export default () => {
  // create store and persistor per normal...

  if (module.hot) {
    module.hot.accept('./path/to/reducer', () => {
      // This fetch the new state of the above reducers.
      const nextRootReducer = require('./path/to/reducer').default
      store.replaceReducer(
        persistReducer(persistConfig, nextRootReducer)
      )
    })
  }

  return { store, persistor }
}
```


================================================
FILE: docs/migrations.md
================================================
# Redux Persist Migration Example

### Example with createMigrate
```js
import { createMigrate, persistReducer, persistStore } from 'redux-persist'
import storage from 'redux-persist/es/storage'

const migrations = {
  0: (state) => {
    // migration clear out device state
    return {
      ...state,
      device: undefined   
    }
  },
  1: (state) => {
    // migration to keep only device state
    return {
      device: state.device
    }
  }
}

const persistConfig = {
  key: 'primary',
  version: 1,
  storage,
  migrate: createMigrate(migrations, { debug: false }),
}

const finalReducer = persistReducer(persistConfig, reducer)

export default function configureStore() {
  let store = createStore(finalReducer)
  let persistor = persistStore(store)
  return { store, persistor }
}
```

### Alternative
The migrate method can be any function with which returns a promise of new state. 
```js
const persistConfig = {
  key: 'primary',
  version: 1,
  storage,
  migrate: (state) => {
    console.log('Migration Running!')
    return Promise.resolve(state)
  }
}


================================================
FILE: docs/v5-migration-alternate.md
================================================
## Alternate Migration
If in redux-persist you used getStoredState + createPersistor, the v5 usage is similar with some small modifications. Note: because no `persistor` is created the react integration helper `PersistGate` cannot be used.

1. replace `createPersistor` with `createPersistoid`
2. update persistoid whenever state changes

```js
import { getStoredState } from 'redux-persist/es/getStoredState'
import { createPersistoid } from 'redux-persist/es/createPersistoid'
import storage from 'redux-persist/es/storages/local'

// ...

const config = { key: 'root', version: 1, storage }

function configureStore () {
  const initState = await getStoredState(config)
  // createPersistoid instead of createPersistor
  let persistoid = createPersistoid(config)

  const store = createStore(reducer, initState)
 
  // need to hook up the subscription (this used to be done automatically by createPersistor)
  store.subscribe(() => {
    persistoid.update(store.getState())
  })
}
```

================================================
FILE: integration/README.md
================================================
Proxy package to enable
```js
import { PersistGate } from 'redux-persist/integration/react'
```

================================================
FILE: integration/react/package.json
================================================
{
    "name": "redux-persist/integration/react",
    "private": true,
    "main": "../../lib/integration/react",
    "module": "../../es/integration/react",
    "jsnext:main": "../../es/integration/react"
  }


================================================
FILE: package.json
================================================
{
  "name": "redux-persist",
  "version": "6.1.0",
  "description": "persist and rehydrate redux stores",
  "main": "lib/index.js",
  "module": "es/index.js",
  "types": "lib/index.d.ts",
  "repository": "rt2zz/redux-persist",
  "files": [
    "src",
    "es",
    "lib",
    "dist",
    "integration",
    "README.md"
  ],
  "scripts": {
    "ava": "ava",
    "build": "npm run build:commonjs && npm run build:es && npm run build:umd",
    "build:commonjs": "tsc --module commonjs --outDir lib",
    "build:es": "tsc --module es2015 --outDir es",
    "build:umd": "rollup -c",
    "clean": "rimraf dist && rimraf es && rimraf lib",
    "prepare": "npm run build",
    "precommit": "lint-staged",
    "stats:size": "node ./scripts/size-estimator.js",
    "test": "ava",
    "version": "npm run clean && npm run build && npm run stats:size | tail -1 >> LIBSIZE.md && git add LIBSIZE.md"
  },
  "lint-staged": {
    "src/**/*.ts": [
      "prettier --write",
      "git add"
    ]
  },
  "author": "",
  "license": "MIT",
  "homepage": "https://github.com/rt2zz/redux-persist#readme",
  "ava": {
    "files": [
      "tests/**/*.spec.ts"
    ],
    "extensions": [
      "ts"
    ],
    "require": [
      "ts-node/register"
    ]
  },
  "devDependencies": {
    "@babel/core": "^7.15.0",
    "@babel/preset-env": "^7.15.0",
    "@rollup/plugin-babel": "^5.3.0",
    "@rollup/plugin-commonjs": "^20.0.0",
    "@rollup/plugin-node-resolve": "^13.0.4",
    "@rollup/plugin-typescript": "^8.2.5",
    "@types/react": "^17.0.16",
    "@types/redux-mock-store": "^1.0.3",
    "@types/sinon": "^10.0.2",
    "@typescript-eslint/eslint-plugin": "^4.29.0",
    "@typescript-eslint/parser": "^4.29.0",
    "ava": "^3.15.0",
    "eslint": "^7.32.0",
    "eslint-plugin-import": "^2.23.4",
    "husky": "^7.0.1",
    "lint-staged": "^11.1.2",
    "prettier": "^2.3.2",
    "redux": "^4.1.1",
    "redux-mock-store": "^1.5.4",
    "rimraf": "^3.0.2",
    "rollup": "^2.56.0",
    "rollup-plugin-terser": "^7.0.2",
    "sinon": "^11.1.2",
    "ts-node": "^10.1.0",
    "typescript": "^4.3.5"
  },
  "peerDependencies": {
    "redux": ">4.0.0"
  }
}


================================================
FILE: rollup.config.js
================================================
import pluginNodeResolve from "@rollup/plugin-node-resolve"
import pluginCommonjs from "@rollup/plugin-commonjs"
import pluginTypescript from "@rollup/plugin-typescript"
import { babel as pluginBabel } from "@rollup/plugin-babel"
import { terser as pluginTerser } from "rollup-plugin-terser"

const moduleName = 'ReduxPersist'

import * as path from 'path'

import pkg from "./package.json"

const banner = `/*!
  ${moduleName}.js v${pkg.version}
  ${pkg.homepage}
  Released under the ${pkg.license} License.
*/`;

const filePath = 'dist/redux-persist.js'

const config = [
  // browser
  {
    // entry point
    input: 'src/index.ts',
    output: [
      // no minify
      {
        name: moduleName,
        file: filePath,
        format: 'umd',
        sourcemap: true,
        // copyright
        banner,
      },
      // minify
      {
        name: moduleName,
        file: filePath.replace('.js', '.min.js'),
        format: 'umd',
        sourcemap: true,
        banner,
        plugins: [
          pluginTerser(),
        ],
      }
    ],
    plugins: [
      pluginTypescript({
        module: "esnext"
      }),
      pluginCommonjs({
        extensions: [".js", ".ts"]
      }),
      pluginBabel({
        babelHelpers: "bundled",
        configFile: path.resolve(__dirname, ".babelrc.js")
      }),
      pluginNodeResolve({
        browser: true,
      }),
    ]
  },
];

export default config



================================================
FILE: scripts/size-estimator.js
================================================
const { execSync }  = require('child_process')
const packageJson = require('../package.json')

let packageVersion = packageJson.version
// we estimate redux size based on the content length of the minified umd build hosted by unpkg. This script is brittle but works.
let reduxSize = execSync("curl -sIL https://unpkg.com/redux/dist/redux.min.js | grep -i Content-Length | tail -1 | awk '{print $2}'").toString()
// we need to substract redux size from our umd build to get an estimate of our first party code size
let persistSize = execSync("wc -c < dist/redux-persist.min.js") - reduxSize

// note: markdown formatted for conveinence when appending to LIBSIZE.md
console.log(`**v${packageVersion}**: ${persistSize} Bytes  `)


================================================
FILE: src/constants.ts
================================================
export const KEY_PREFIX = 'persist:'
export const FLUSH = 'persist/FLUSH'
export const REHYDRATE = 'persist/REHYDRATE'
export const PAUSE = 'persist/PAUSE'
export const PERSIST = 'persist/PERSIST'
export const PURGE = 'persist/PURGE'
export const REGISTER = 'persist/REGISTER'
export const DEFAULT_VERSION = -1


================================================
FILE: src/createMigrate.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
import { DEFAULT_VERSION } from './constants'

import type { PersistedState, MigrationManifest } from './types'

export default function createMigrate(
  migrations: MigrationManifest,
  config?: { debug: boolean }
): (state: PersistedState, currentVersion: number) => Promise<PersistedState> {
  const { debug } = config || {}
  return function(
    state: PersistedState,
    currentVersion: number
  ): Promise<PersistedState> {
    if (!state) {
      if (process.env.NODE_ENV !== 'production' && debug)
        console.log('redux-persist: no inbound state, skipping migration')
      return Promise.resolve(undefined)
    }

    const inboundVersion: number =
      state._persist && state._persist.version !== undefined
        ? state._persist.version
        : DEFAULT_VERSION
    if (inboundVersion === currentVersion) {
      if (process.env.NODE_ENV !== 'production' && debug)
        console.log('redux-persist: versions match, noop migration')
      return Promise.resolve(state)
    }
    if (inboundVersion > currentVersion) {
      if (process.env.NODE_ENV !== 'production')
        console.error('redux-persist: downgrading version is not supported')
      return Promise.resolve(state)
    }

    const migrationKeys = Object.keys(migrations)
      .map(ver => parseInt(ver))
      .filter(key => currentVersion >= key && key > inboundVersion)
      .sort((a, b) => a - b)

    if (process.env.NODE_ENV !== 'production' && debug)
      console.log('redux-persist: migrationKeys', migrationKeys)
    try {
      const migratedState: any = migrationKeys.reduce((state: any, versionKey) => {
        if (process.env.NODE_ENV !== 'production' && debug)
          console.log(
            'redux-persist: running migration for versionKey',
            versionKey
          )
        return migrations[versionKey](state)
      }, state)
      return Promise.resolve(migratedState)
    } catch (err) {
      return Promise.reject(err)
    }
  }
}


================================================
FILE: src/createPersistoid.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
import { KEY_PREFIX } from './constants'

import type { Persistoid, PersistConfig } from './types'
import { KeyAccessState } from './types'

export default function createPersistoid(config: PersistConfig<any>): Persistoid {
  // defaults
  const blacklist: string[] | null = config.blacklist || null
  const whitelist: string[] | null = config.whitelist || null
  const transforms = config.transforms || []
  const throttle = config.throttle || 0
  const storageKey = `${
    config.keyPrefix !== undefined ? config.keyPrefix : KEY_PREFIX
  }${config.key}`
  const storage = config.storage
  let serialize: (x: any) => any
  if (config.serialize === false) {
    serialize = (x: any) => x
  } else if (typeof config.serialize === 'function') {
    serialize = config.serialize
  } else {
    serialize = defaultSerialize
  }
  const writeFailHandler = config.writeFailHandler || null

  // initialize stateful values
  let lastState: KeyAccessState = {}
  const stagedState: KeyAccessState = {}
  const keysToProcess: string[] = []
  let timeIterator: any = null
  let writePromise: Promise<any> | null = null

  const update = (state: KeyAccessState) => {
    // add any changed keys to the queue
    Object.keys(state).forEach(key => {
      if (!passWhitelistBlacklist(key)) return // is keyspace ignored? noop
      if (lastState[key] === state[key]) return // value unchanged? noop
      if (keysToProcess.indexOf(key) !== -1) return // is key already queued? noop
      keysToProcess.push(key) // add key to queue
    })

    //if any key is missing in the new state which was present in the lastState,
    //add it for processing too
    Object.keys(lastState).forEach(key => {
      if (
        state[key] === undefined &&
        passWhitelistBlacklist(key) &&
        keysToProcess.indexOf(key) === -1 &&
        lastState[key] !== undefined
      ) {
        keysToProcess.push(key)
      }
    })

    // start the time iterator if not running (read: throttle)
    if (timeIterator === null) {
      timeIterator = setInterval(processNextKey, throttle)
    }

    lastState = state
  }

  function processNextKey() {
    if (keysToProcess.length === 0) {
      if (timeIterator) clearInterval(timeIterator)
      timeIterator = null
      return
    }

    const key: any = keysToProcess.shift()
    if (key === undefined) {
      return
    }
    const endState = transforms.reduce((subState, transformer) => {
      return transformer.in(subState, key, lastState)
    }, lastState[key])

    if (endState !== undefined) {
      try {
        stagedState[key] = serialize(endState)
      } catch (err) {
        console.error(
          'redux-persist/createPersistoid: error serializing state',
          err
        )
      }
    } else {
      //if the endState is undefined, no need to persist the existing serialized content
      delete stagedState[key]
    }

    if (keysToProcess.length === 0) {
      writeStagedState()
    }
  }

  function writeStagedState() {
    // cleanup any removed keys just before write.
    Object.keys(stagedState).forEach(key => {
      if (lastState[key] === undefined) {
        delete stagedState[key]
      }
    })

    writePromise = storage
      .setItem(storageKey, serialize(stagedState))
      .catch(onWriteFail)
  }

  function passWhitelistBlacklist(key: string) {
    if (whitelist && whitelist.indexOf(key) === -1 && key !== '_persist')
      return false
    if (blacklist && blacklist.indexOf(key) !== -1) return false
    return true
  }

  function onWriteFail(err: any) {
    // @TODO add fail handlers (typically storage full)
    if (writeFailHandler) writeFailHandler(err)
    if (err && process.env.NODE_ENV !== 'production') {
      console.error('Error storing data', err)
    }
  }

  const flush = () => {
    while (keysToProcess.length !== 0) {
      processNextKey()
    }
    return writePromise || Promise.resolve()
  }

  // return `persistoid`
  return {
    update,
    flush,
  }
}

// @NOTE in the future this may be exposed via config
function defaultSerialize(data: any) {
  return JSON.stringify(data)
}


================================================
FILE: src/createTransform.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
type TransformConfig = {
  whitelist?: Array<string>,
  blacklist?: Array<string>,
}

export default function createTransform(
  // @NOTE inbound: transform state coming from redux on its way to being serialized and stored
  // eslint-disable-next-line @typescript-eslint/ban-types
  inbound: Function,
  // @NOTE outbound: transform state coming from storage, on its way to be rehydrated into redux
  // eslint-disable-next-line @typescript-eslint/ban-types
  outbound: Function,
  config: TransformConfig = {}
): any {
  const whitelist = config.whitelist || null
  const blacklist = config.blacklist || null

  function whitelistBlacklistCheck(key: string) {
    if (whitelist && whitelist.indexOf(key) === -1) return true
    if (blacklist && blacklist.indexOf(key) !== -1) return true
    return false
  }

  return {
    in: (state: Record<string, unknown>, key: string, fullState: Record<string, unknown>) =>
      !whitelistBlacklistCheck(key) && inbound
        ? inbound(state, key, fullState)
        : state,
    out: (state: Record<string, unknown>, key: string, fullState: Record<string, unknown>) =>
      !whitelistBlacklistCheck(key) && outbound
        ? outbound(state, key, fullState)
        : state,
  }
}


================================================
FILE: src/getStoredState.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { KeyAccessState, PersistConfig } from './types'

import { KEY_PREFIX } from './constants'

export default function getStoredState(
  config: PersistConfig<any>
): Promise<any | void> {
  const transforms = config.transforms || []
  const storageKey = `${
    config.keyPrefix !== undefined ? config.keyPrefix : KEY_PREFIX
  }${config.key}`
  const storage = config.storage
  const debug = config.debug
  let deserialize: (x: any) => any
  if (config.deserialize === false) {
    deserialize = (x: any) => x
  } else if (typeof config.deserialize === 'function') {
    deserialize = config.deserialize
  } else {
    deserialize = defaultDeserialize
  }
  return storage.getItem(storageKey).then((serialized: any) => {
    if (!serialized) return undefined
    else {
      try {
        const state: KeyAccessState = {}
        const rawState = deserialize(serialized)
        Object.keys(rawState).forEach(key => {
          state[key] = transforms.reduceRight((subState, transformer) => {
            return transformer.out(subState, key, rawState)
          }, deserialize(rawState[key]))
        })
        return state
      } catch (err) {
        if (process.env.NODE_ENV !== 'production' && debug)
          console.log(
            `redux-persist/getStoredState: Error restoring data ${serialized}`,
            err
          )
        throw err
      }
    }
  })
}

function defaultDeserialize(serial: string) {
  return JSON.parse(serial)
}


================================================
FILE: src/index.ts
================================================
export { default as persistReducer } from './persistReducer'
export { default as persistCombineReducers } from './persistCombineReducers'
export { default as persistStore } from './persistStore'
export { default as createMigrate } from './createMigrate'
export { default as createTransform } from './createTransform'
export { default as getStoredState } from './getStoredState'
export { default as createPersistoid } from './createPersistoid'
export { default as purgeStoredState } from './purgeStoredState'

export * from './constants'


================================================
FILE: src/integration/getStoredStateMigrateV4.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
import getStoredStateV5 from '../getStoredState'

import type { KeyAccessState, PersistConfig, Storage, Transform } from '../types'

type V4Config = {
  storage?: Storage,
  serialize: boolean,
  keyPrefix?: string,
  transforms?: Array<Transform<any, any>>,
  blacklist?: Array<string>,
  whitelist?: Array<string>,
}

export default function getStoredState(v4Config: V4Config) {
  return function(v5Config: PersistConfig<any>): any {
    return getStoredStateV5(v5Config).then(state => {
      if (state) return state
      else return getStoredStateV4(v4Config)
    })
  }
}

const KEY_PREFIX = 'reduxPersist:'

function hasLocalStorage() {
  if (typeof self !== 'object' || !('localStorage' in self)) {
    return false
  }

  try {
    const storage = self.localStorage
    const testKey = `redux-persist localStorage test`
    storage.setItem(testKey, 'test')
    storage.getItem(testKey)
    storage.removeItem(testKey)
  } catch (e) {
    if (process.env.NODE_ENV !== 'production')
      console.warn(
        `redux-persist localStorage test failed, persistence will be disabled.`
      )
    return false
  }
  return true
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const noop = (...args: any) => {
  /* noop */ return null
}
const noStorage = {
  getItem: noop,
  setItem: noop,
  removeItem: noop,
  getAllKeys: noop,
  keys: []
}
const createAsyncLocalStorage = () => {
  if (!hasLocalStorage()) return noStorage
  const localStorage = self.localStorage
  return {
    getAllKeys: function(cb: any) {
      try {
        const keys = []
        for (let i = 0; i < localStorage.length; i++) {
          keys.push(localStorage.key(i))
        }
        cb(null, keys)
      } catch (e) {
        cb(e)
      }
    },
    getItem(key: string, cb: any) {
      try {
        const s = localStorage.getItem(key)
        cb(null, s)
      } catch (e) {
        cb(e)
      }
    },
    setItem(key: string, string: string, cb: any) {
      try {
        localStorage.setItem(key, string)
        cb(null)
      } catch (e) {
        cb(e)
      }
    },
    removeItem(key: string, cb: any) {
      try {
        localStorage.removeItem(key)
        cb && cb(null)
      } catch (e) {
        cb(e)
      }
    },
    keys: localStorage.keys
  }
}

function getStoredStateV4(v4Config: V4Config) {
  return new Promise((resolve, reject) => {
    let storage = v4Config.storage || createAsyncLocalStorage()
    const deserializer =
      v4Config.serialize === false
        ? (data: any) => data
        : (serial: string) => JSON.parse(serial)
    const blacklist = v4Config.blacklist || []
    const whitelist = v4Config.whitelist || false
    const transforms = v4Config.transforms || []
    const keyPrefix =
      v4Config.keyPrefix !== undefined ? v4Config.keyPrefix : KEY_PREFIX

    // fallback getAllKeys to `keys` if present (LocalForage compatability)
    if (storage.keys && !storage.getAllKeys)
      storage = { ...storage, getAllKeys: storage.keys }

    const restoredState: KeyAccessState = {}
    let completionCount = 0

    storage.getAllKeys((err: any, allKeys:string[] = []) => {
      if (err) {
        if (process.env.NODE_ENV !== 'production')
          console.warn(
            'redux-persist/getStoredState: Error in storage.getAllKeys'
          )
        return reject(err)
      }

      const persistKeys = allKeys
        .filter(key => key.indexOf(keyPrefix) === 0)
        .map(key => key.slice(keyPrefix.length))
      const keysToRestore = persistKeys.filter(passWhitelistBlacklist)

      const restoreCount = keysToRestore.length
      if (restoreCount === 0) resolve(undefined)
      keysToRestore.forEach(key => {
        storage.getItem(createStorageKey(key), (err: any, serialized: string) => {
          if (err && process.env.NODE_ENV !== 'production')
            console.warn(
              'redux-persist/getStoredState: Error restoring data for key:',
              key,
              err
            )
          else restoredState[key] = rehydrate(key, serialized)
          completionCount += 1
          if (completionCount === restoreCount) resolve(restoredState)
        })
      })
    })

    function rehydrate(key: string, serialized: string) {
      let state = null

      try {
        const data = serialized ? deserializer(serialized) : undefined
        state = transforms.reduceRight((subState, transformer) => {
          return transformer.out(subState, key, {})
        }, data)
      } catch (err) {
        if (process.env.NODE_ENV !== 'production')
          console.warn(
            'redux-persist/getStoredState: Error restoring data for key:',
            key,
            err
          )
      }

      return state
    }

    function passWhitelistBlacklist(key: string) {
      if (whitelist && whitelist.indexOf(key) === -1) return false
      if (blacklist.indexOf(key) !== -1) return false
      return true
    }

    function createStorageKey(key: string) {
      return `${keyPrefix}${key}`
    }
  })
}


================================================
FILE: src/integration/react.ts
================================================
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import React, { PureComponent, ReactNode } from 'react'
import type { Persistor } from '../types'

type Props = {
  onBeforeLift?: () => void,
  children: ReactNode | ((state: boolean) => ReactNode),
  loading: ReactNode,
  persistor: Persistor,
}

type State = {
  bootstrapped: boolean,
}

export class PersistGate extends PureComponent<Props, State> {
  static defaultProps = {
    children: null,
    loading: null,
  }

  state = {
    bootstrapped: false,
  }
  _unsubscribe?: () => void

  componentDidMount(): void {
    this._unsubscribe = this.props.persistor.subscribe(
      this.handlePersistorState
    )
    this.handlePersistorState()
  }

  handlePersistorState = (): void => {
    const { persistor } = this.props
    const { bootstrapped } = persistor.getState()
    if (bootstrapped) {
      if (this.props.onBeforeLift) {
        Promise.resolve(this.props.onBeforeLift())
          .finally(() => this.setState({ bootstrapped: true }))
      } else {
        this.setState({ bootstrapped: true })
      }
      this._unsubscribe && this._unsubscribe()
    }
  }

  componentWillUnmount(): void {
    this._unsubscribe && this._unsubscribe()
  }

  render(): ReactNode {
    if (process.env.NODE_ENV !== 'production') {
      if (typeof this.props.children === 'function' && this.props.loading)
        console.error(
          'redux-persist: PersistGate expects either a function child or loading prop, but not both. The loading prop will be ignored.'
        )
    }
    if (typeof this.props.children === 'function') {
      return this.props.children(this.state.bootstrapped)
    }

    return this.state.bootstrapped ? this.props.children : this.props.loading
  }
}


================================================
FILE: src/persistCombineReducers.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Action, AnyAction, CombinedState, combineReducers, Reducer, ReducersMapObject } from 'redux'
import persistReducer from './persistReducer'
import autoMergeLevel2 from './stateReconciler/autoMergeLevel2'

import type { 
  PersistConfig
} from './types'

// combineReducers + persistReducer with stateReconciler defaulted to autoMergeLevel2
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export default function persistCombineReducers<S, A extends Action>(
  config: PersistConfig<any>,
  reducers: ReducersMapObject<CombinedState<S>, Action<any>>
): Reducer<any, AnyAction> {
  config.stateReconciler =
    config.stateReconciler === undefined
      ? autoMergeLevel2
      : config.stateReconciler
  return persistReducer(config, combineReducers(reducers))
}


================================================
FILE: src/persistReducer.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
  Action, AnyAction, Reducer
} from 'redux'

import {
  FLUSH,
  PAUSE,
  PERSIST,
  PURGE,
  REHYDRATE,
  DEFAULT_VERSION,
} from './constants'

import type {
  PersistConfig,
  PersistState,
  Persistoid,
} from './types'

import autoMergeLevel1 from './stateReconciler/autoMergeLevel1'
import createPersistoid from './createPersistoid'
import defaultGetStoredState from './getStoredState'
import purgeStoredState from './purgeStoredState'

type PersistPartial = { _persist: PersistState } | any;
const DEFAULT_TIMEOUT = 5000
/*
  @TODO add validation / handling for:
  - persisting a reducer which has nested _persist
  - handling actions that fire before reydrate is called
*/
export default function persistReducer<S, A extends Action>(
  config: PersistConfig<S>,
  baseReducer: Reducer<S, A>
): Reducer<S & PersistPartial, AnyAction> {
  if (process.env.NODE_ENV !== 'production') {
    if (!config) throw new Error('config is required for persistReducer')
    if (!config.key) throw new Error('key is required in persistor config')
    if (!config.storage)
      throw new Error(
        "redux-persist: config.storage is required. Try using one of the provided storage engines `import storage from 'redux-persist/lib/storage'`"
      )
  }

  const version =
    config.version !== undefined ? config.version : DEFAULT_VERSION
  const stateReconciler =
    config.stateReconciler === undefined
      ? autoMergeLevel1
      : config.stateReconciler
  const getStoredState = config.getStoredState || defaultGetStoredState
  const timeout =
    config.timeout !== undefined ? config.timeout : DEFAULT_TIMEOUT
  let _persistoid: Persistoid | null = null
  let _purge = false
  let _paused = true
  const conditionalUpdate = (state: any) => {
    // update the persistoid only if we are rehydrated and not paused
    state._persist.rehydrated &&
      _persistoid &&
      !_paused &&
      _persistoid.update(state)
    return state
  }

  return (state: any, action: any) => {
    const { _persist, ...rest } = state || {}
    const restState: S = rest

    if (action.type === PERSIST) {
      let _sealed = false
      const _rehydrate = (payload: any, err?: Error) => {
        // dev warning if we are already sealed
        if (process.env.NODE_ENV !== 'production' && _sealed)
          console.error(
            `redux-persist: rehydrate for "${
              config.key
            }" called after timeout.`,
            payload,
            err
          )

        // only rehydrate if we are not already sealed
        if (!_sealed) {
          action.rehydrate(config.key, payload, err)
          _sealed = true
        }
      }
      timeout &&
        setTimeout(() => {
          !_sealed &&
            _rehydrate(
              undefined,
              new Error(
                `redux-persist: persist timed out for persist key "${
                  config.key
                }"`
              )
            )
        }, timeout)

      // @NOTE PERSIST resumes if paused.
      _paused = false

      // @NOTE only ever create persistoid once, ensure we call it at least once, even if _persist has already been set
      if (!_persistoid) _persistoid = createPersistoid(config)

      // @NOTE PERSIST can be called multiple times, noop after the first
      if (_persist) {
        // We still need to call the base reducer because there might be nested
        // uses of persistReducer which need to be aware of the PERSIST action
        return {
          ...baseReducer(restState, action),
          _persist,
        };
      }

      if (
        typeof action.rehydrate !== 'function' ||
        typeof action.register !== 'function'
      )
        throw new Error(
          'redux-persist: either rehydrate or register is not a function on the PERSIST action. This can happen if the action is being replayed. This is an unexplored use case, please open an issue and we will figure out a resolution.'
        )

      action.register(config.key)

      getStoredState(config).then(
        restoredState => {
          if (restoredState) {
            // eslint-disable-next-line @typescript-eslint/no-unused-vars
            const migrate = config.migrate || ((s, _) => Promise.resolve(s))
            migrate(restoredState as any, version).then(
              migratedState => {
                _rehydrate(migratedState)
              },
              migrateErr => {
                if (process.env.NODE_ENV !== 'production' && migrateErr)
                  console.error('redux-persist: migration error', migrateErr)
                _rehydrate(undefined, migrateErr)
              }
            )
          }
        },
        err => {
          _rehydrate(undefined, err)
        }
      )

      return {
        ...baseReducer(restState, action),
        _persist: { version, rehydrated: false },
      }
    } else if (action.type === PURGE) {
      _purge = true
      action.result(purgeStoredState(config))
      return {
        ...baseReducer(restState, action),
        _persist,
      }
    } else if (action.type === FLUSH) {
      action.result(_persistoid && _persistoid.flush())
      return {
        ...baseReducer(restState, action),
        _persist,
      }
    } else if (action.type === PAUSE) {
      _paused = true
    } else if (action.type === REHYDRATE) {
      // noop on restState if purging
      if (_purge)
        return {
          ...restState,
          _persist: { ..._persist, rehydrated: true },
        }

      // @NOTE if key does not match, will continue to default else below
      if (action.key === config.key) {
        const reducedState = baseReducer(restState, action)
        const inboundState = action.payload
        // only reconcile state if stateReconciler and inboundState are both defined
        const reconciledRest: S =
          stateReconciler !== false && inboundState !== undefined
            ? stateReconciler(inboundState, state, reducedState, config)
            : reducedState

        const newState = {
          ...reconciledRest,
          _persist: { ..._persist, rehydrated: true },
        }
        return conditionalUpdate(newState)
      }
    }

    // if we have not already handled PERSIST, straight passthrough
    if (!_persist) return baseReducer(state, action)

    // run base reducer:
    // is state modified ? return original : return updated
    const newState = baseReducer(restState, action)
    if (newState === restState) return state
    return conditionalUpdate({ ...newState, _persist })
  }
}


================================================
FILE: src/persistStore.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
import type {
  Persistor,
  PersistorOptions,
  PersistorState,
} from './types'

import { AnyAction, createStore, Store } from 'redux'
import { FLUSH, PAUSE, PERSIST, PURGE, REGISTER, REHYDRATE } from './constants'

type BoostrappedCb = () => any;

const initialState: PersistorState = {
  registry: [],
  bootstrapped: false,
}

const persistorReducer = (state = initialState, action: AnyAction) => {
  const firstIndex = state.registry.indexOf(action.key)
  const registry = [...state.registry]
  switch (action.type) {
    case REGISTER:
      return { ...state, registry: [...state.registry, action.key] }
    case REHYDRATE:
      registry.splice(firstIndex, 1)
      return { ...state, registry, bootstrapped: registry.length === 0 }
    default:
      return state
  }
}

interface OptionToTestObject {
  [key: string]: any;
}

export default function persistStore(
  store: Store,
  options?: PersistorOptions,
  cb?: BoostrappedCb
): Persistor {
  // help catch incorrect usage of passing PersistConfig in as PersistorOptions
  if (process.env.NODE_ENV !== 'production') {
    const optionsToTest: OptionToTestObject = options || {}
    const bannedKeys = [
      'blacklist',
      'whitelist',
      'transforms',
      'storage',
      'keyPrefix',
      'migrate',
    ]
    bannedKeys.forEach(k => {
      if (optionsToTest[k])
        console.error(
          `redux-persist: invalid option passed to persistStore: "${k}". You may be incorrectly passing persistConfig into persistStore, whereas it should be passed into persistReducer.`
        )
    })
  }
  let boostrappedCb = cb || false

  const _pStore = createStore(
    persistorReducer,
    initialState,
    options && options.enhancer ? options.enhancer : undefined
  )
  const register = (key: string) => {
    _pStore.dispatch({
      type: REGISTER,
      key,
    })
  }

  const rehydrate = (key: string, payload: Record<string, unknown>, err: any) => {
    const rehydrateAction = {
      type: REHYDRATE,
      payload,
      err,
      key,
    }
    // dispatch to `store` to rehydrate and `persistor` to track result
    store.dispatch(rehydrateAction)
    _pStore.dispatch(rehydrateAction)
    if (typeof boostrappedCb === "function" && persistor.getState().bootstrapped) {
      boostrappedCb()
      boostrappedCb = false
    }
  }

  const persistor: Persistor = {
    ..._pStore,
    purge: () => {
      const results: Array<any> = []
      store.dispatch({
        type: PURGE,
        result: (purgeResult: any) => {
          results.push(purgeResult)
        },
      })
      return Promise.all(results)
    },
    flush: () => {
      const results: Array<any> = []
      store.dispatch({
        type: FLUSH,
        result: (flushResult: any) => {
          results.push(flushResult)
        },
      })
      return Promise.all(results)
    },
    pause: () => {
      store.dispatch({
        type: PAUSE,
      })
    },
    persist: () => {
      store.dispatch({ type: PERSIST, register, rehydrate })
    },
  }

  if (!(options && options.manualPersist)){
    persistor.persist()
  }

  return persistor
}


================================================
FILE: src/purgeStoredState.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { PersistConfig } from './types'

import { KEY_PREFIX } from './constants'

export default function purgeStoredState(config: PersistConfig<any>):any {
  const storage = config.storage
  const storageKey = `${
    config.keyPrefix !== undefined ? config.keyPrefix : KEY_PREFIX
  }${config.key}`
  return storage.removeItem(storageKey, warnIfRemoveError)
}

function warnIfRemoveError(err: any) {
  if (err && process.env.NODE_ENV !== 'production') {
    console.error(
      'redux-persist/purgeStoredState: Error purging data stored state',
      err
    )
  }
}


================================================
FILE: src/stateReconciler/autoMergeLevel1.ts
================================================
/*
  autoMergeLevel1: 
    - merges 1 level of substate
    - skips substate if already modified
*/

import type { PersistConfig } from '../types'
import { KeyAccessState } from '../types'

export default function autoMergeLevel1<S extends KeyAccessState>(
  inboundState: S,
  originalState: S,
  reducedState: S,
  { debug }: PersistConfig<S>
): S {
  const newState = { ...reducedState }
  // only rehydrate if inboundState exists and is an object
  if (inboundState && typeof inboundState === 'object') {
    const keys: (keyof S)[] = Object.keys(inboundState)
    keys.forEach(key => {
      // ignore _persist data
      if (key === '_persist') return
      // if reducer modifies substate, skip auto rehydration
      if (originalState[key] !== reducedState[key]) {
        if (process.env.NODE_ENV !== 'production' && debug)
          console.log(
            'redux-persist/stateReconciler: sub state for key `%s` modified, skipping.',
            key
          )
        return
      }
      // otherwise hard set the new value
      newState[key] = inboundState[key]
    })
  }

  if (
    process.env.NODE_ENV !== 'production' &&
    debug &&
    inboundState &&
    typeof inboundState === 'object'
  )
    console.log(
      `redux-persist/stateReconciler: rehydrated keys '${Object.keys(
        inboundState
      ).join(', ')}'`
    )

  return newState
}


================================================
FILE: src/stateReconciler/autoMergeLevel2.ts
================================================
/*
  autoMergeLevel2: 
    - merges 2 level of substate
    - skips substate if already modified
    - this is essentially redux-perist v4 behavior
*/

import type { PersistConfig } from '../types'
import { KeyAccessState } from '../types'

export default function autoMergeLevel2<S extends KeyAccessState>(
  inboundState: S,
  originalState: S,
  reducedState: S,
  { debug }: PersistConfig<S>
): S {
  const newState = { ...reducedState }
  // only rehydrate if inboundState exists and is an object
  if (inboundState && typeof inboundState === 'object') {
    const keys: (keyof S)[] = Object.keys(inboundState)
    keys.forEach(key => {
      // ignore _persist data
      if (key === '_persist') return
      // if reducer modifies substate, skip auto rehydration
      if (originalState[key] !== reducedState[key]) {
        if (process.env.NODE_ENV !== 'production' && debug)
          console.log(
            'redux-persist/stateReconciler: sub state for key `%s` modified, skipping.',
            key
          )
        return
      }
      if (isPlainEnoughObject(reducedState[key])) {
        // if object is plain enough shallow merge the new values (hence "Level2")
        newState[key] = { ...newState[key], ...inboundState[key] }
        return
      }
      // otherwise hard set
      newState[key] = inboundState[key]
    })
  }

  if (
    process.env.NODE_ENV !== 'production' &&
    debug &&
    inboundState &&
    typeof inboundState === 'object'
  )
    console.log(
      `redux-persist/stateReconciler: rehydrated keys '${Object.keys(
        inboundState
      ).join(', ')}'`
    )

  return newState
}

function isPlainEnoughObject(o: unknown) {
  return o !== null && !Array.isArray(o) && typeof o === 'object'
}


================================================
FILE: src/stateReconciler/hardSet.ts
================================================
/*
  hardSet: 
    - hard set incoming state
*/

export default function hardSet<S>(inboundState: S): S {
  return inboundState
}


================================================
FILE: src/storage/createWebStorage.ts
================================================
import getStorage from './getStorage'

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export default function createWebStorage(type: string): any {
  const storage = getStorage(type)
  return {
    getItem: (key: string): Promise<string> => {
      return new Promise((resolve) => {
        resolve(storage.getItem(key))
      })
    },
    setItem: (key: string, item: string): Promise<void> => {
      return new Promise((resolve) => {
        resolve(storage.setItem(key, item))
      })
    },
    removeItem: (key: string): Promise<void> => {
      return new Promise((resolve) => {
        resolve(storage.removeItem(key))
      })
    },
  }
}


================================================
FILE: src/storage/getStorage.ts
================================================
import type { Storage } from '../types'

// eslint-disable-next-line @typescript-eslint/no-empty-function
function noop() {}
const noopStorage = {
  getItem: noop,
  setItem: noop,
  removeItem: noop,
  keys: [],
  getAllKeys: noop,
}

function hasStorage(storageType: string) {
  if (typeof self !== 'object' || !(storageType in self)) {
    return false
  }

  try {
    const storage = (self as unknown as { [key: string]: Storage})[storageType] as unknown as Storage
    const testKey = `redux-persist ${storageType} test`
    storage.setItem(testKey, 'test')
    storage.getItem(testKey)
    storage.removeItem(testKey)
  } catch (e) {
    if (process.env.NODE_ENV !== 'production')
      console.warn(
        `redux-persist ${storageType} test failed, persistence will be disabled.`
      )
    return false
  }
  return true
}

export default function getStorage(type: string): Storage {
  const storageType = `${type}Storage`
  if (hasStorage(storageType)) return (self as unknown as { [key: string]: Storage })[storageType]
  else {
    if (process.env.NODE_ENV !== 'production') {
      console.error(
        `redux-persist failed to create sync storage. falling back to noop storage.`
      )
    }
    return noopStorage
  }
}


================================================
FILE: src/storage/index.ts
================================================
import createWebStorage from './createWebStorage'

export default createWebStorage('local')


================================================
FILE: src/storage/session.ts
================================================
import createWebStorage from './createWebStorage'

export default createWebStorage('session')


================================================
FILE: src/types.ts
================================================
/* eslint-disable @typescript-eslint/ban-types */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { REHYDRATE, REGISTER } from './constants'

import { StoreEnhancer } from "redux";

export interface PersistState {
  version: number;
  rehydrated: boolean;
}

export type PersistedState = {
  _persist: PersistState;
} | undefined;

export type PersistMigrate =
  (state: PersistedState, currentVersion: number) => Promise<PersistedState>;

export type StateReconciler<S> =
  (inboundState: any, state: S, reducedState: S, config: PersistConfig<S>) => S;

export interface KeyAccessState {
  [key: string]: any;
}

/**
 * @desc
 * `HSS` means HydratedSubState
 * `ESS` means EndSubState
 * `S` means State
 * `RS` means RawState
 */
export interface PersistConfig<S, RS = any, HSS = any, ESS = any> {
  version?: number;
  storage: Storage;
  key: string;
  /**
   * @deprecated keyPrefix is going to be removed in v6.
   */
  keyPrefix?: string;
  blacklist?: Array<string>;
  whitelist?: Array<string>;
  transforms?: Array<Transform<HSS, ESS, S, RS>>;
  throttle?: number;
  migrate?: PersistMigrate;
  stateReconciler?: false | StateReconciler<S>;
  /**
   * @desc Used for migrations.
   */
  getStoredState?: (config: PersistConfig<S, RS, HSS, ESS>) => Promise<PersistedState>;
  debug?: boolean;
  serialize?: boolean;
  deserialize?: boolean | ((x: any) => any);
  timeout?: number;
  writeFailHandler?: (err: Error) => void;
}

export interface PersistorOptions {
  enhancer?: StoreEnhancer<any>;
  manualPersist?: boolean;
}

export interface Storage {
  getItem(key: string, ...args: Array<any>): any;
  setItem(key: string, value: any, ...args: Array<any>): any;
  removeItem(key: string, ...args: Array<any>): any;
  keys?: Array<string>;
  getAllKeys(cb?: any): any;
}

export interface WebStorage extends Storage {
  /**
   * @desc Fetches key and returns item in a promise.
   */
  getItem(key: string): Promise<string | null>;
  /**
   * @desc Sets value for key and returns item in a promise.
   */
  setItem(key: string, item: string): Promise<void>;
  /**
   * @desc Removes value for key.
   */
  removeItem(key: string): Promise<void>;
}

export interface MigrationManifest {
  [key: string]: (state: PersistedState) => PersistedState;
}

/**
 * @desc
 * `SS` means SubState
 * `ESS` means EndSubState
 * `S` means State
 */
export type TransformInbound<SS, ESS, S = any> =
  (subState: SS, key: keyof S, state: S) => ESS;

/**
 * @desc
 * `SS` means SubState
 * `HSS` means HydratedSubState
 * `RS` means RawState
 */
export type TransformOutbound<SS, HSS, RS = any> =
  (state: SS, key: keyof RS, rawState: RS) => HSS;

export interface Transform<HSS, ESS, S = any, RS = any> {
  in: TransformInbound<HSS, ESS, S>;
  out: TransformOutbound<ESS, HSS, RS>;
}

export type RehydrateErrorType = any;

export interface RehydrateAction {
  type: typeof REHYDRATE;
  key: string;
  payload?: object | null;
  err?: RehydrateErrorType | null;
}

export interface Persistoid {
  update(state: object): void;
  flush(): Promise<any>;
}

export interface RegisterAction {
  type: typeof REGISTER;
  key: string;
}

export type PersistorAction =
  | RehydrateAction
  | RegisterAction
;

export interface PersistorState {
  registry: Array<string>;
  bootstrapped: boolean;
}

export type PersistorSubscribeCallback = () => any;

/**
 * A persistor is a redux store unto itself, allowing you to purge stored state, flush all
 * pending state serialization and immediately write to disk
 */
export interface Persistor {
  pause(): void;
  persist(): void;
  purge(): Promise<any>;
  flush(): Promise<any>;
  dispatch(action: PersistorAction): PersistorAction;
  getState(): PersistorState;
  subscribe(callback: PersistorSubscribeCallback): () => any;
}


================================================
FILE: tests/complete.spec.ts
================================================
import test from 'ava'
import { combineReducers, createStore } from 'redux'

import persistReducer from '../src/persistReducer'
import persistStore from '../src/persistStore'
import createMemoryStorage from './utils/createMemoryStorage'
import brokenStorage from './utils/brokenStorage'

const reducer = () => ({})
const config = {
  key: 'persist-reducer-test',
  version: 1,
  storage: createMemoryStorage(),
  debug: true,
  timeout: 5,
}

test('multiple persistReducers work together', t => {
  return new Promise((resolve) => {
    const r1 = persistReducer(config, reducer)
    const r2 = persistReducer(config, reducer)
    const rootReducer = combineReducers({ r1, r2 })
    const store = createStore(rootReducer)
    const persistor = persistStore(store, {}, () => {
      t.is(persistor.getState().bootstrapped, true)
      resolve()      
    })
  })
})

test('persistStore timeout 0 never bootstraps', t => {
  return new Promise((resolve, reject) => {
    const r1 = persistReducer({...config, storage: brokenStorage, timeout: 0}, reducer)
    const rootReducer = combineReducers({ r1 })
    const store = createStore(rootReducer)
    const persistor = persistStore(store, undefined, () => {
      console.log('resolve')
      reject()     
    })
    setTimeout(() => {
      t.is(persistor.getState().bootstrapped, false)
      resolve()
    }, 10)
  })
})


test('persistStore timeout forces bootstrap', t => {
  return new Promise((resolve, reject) => {
    const r1 = persistReducer({...config, storage: brokenStorage}, reducer)
    const rootReducer = combineReducers({ r1 })
    const store = createStore(rootReducer)
    const persistor = persistStore(store, undefined, () => {
      t.is(persistor.getState().bootstrapped, true)
      resolve()
    })
    setTimeout(() => {
      reject()
    }, 10)
  })
})


================================================
FILE: tests/createPersistor.spec.ts
================================================
import test from 'ava'
import sinon from 'sinon'
import createMemoryStorage from './utils/createMemoryStorage'
import createPersistoid from '../src/createPersistoid'
const memoryStorage = createMemoryStorage()

const config = {
  key: 'persist-reducer-test',
  version: 1,
  storage: memoryStorage,
  debug: true
}

let spy: sinon.SinonSpy;
let clock: sinon.SinonFakeTimers;

test.beforeEach(() => {
    spy = sinon.spy(memoryStorage, 'setItem')
    clock = sinon.useFakeTimers()
});

test.afterEach(() => {
    spy.restore()
    clock.restore()
});

// @NOTE these tests broke when updating sinon
test.skip('it updates changed state', t => {
    const { update } = createPersistoid(config)
    update({ a: 1 })
    clock.tick(1);
    update({ a: 2 })
    clock.tick(1);
    t.true(spy.calledTwice);
    t.true(spy.withArgs('persist:persist-reducer-test', '{"a":"1"}').calledOnce);
    t.true(spy.withArgs('persist:persist-reducer-test', '{"a":"2"}').calledOnce);
})

test.skip('it does not update unchanged state', t => {
    const { update } = createPersistoid(config)
    update({ a: undefined, b: 1 })
    clock.tick(1);
    // This update should not cause a write.
    update({ a: undefined, b: 1 })
    clock.tick(1);
    t.true(spy.calledOnce);
    t.true(spy.withArgs('persist:persist-reducer-test', '{"b":"1"}').calledOnce);
})

test.skip('it updates removed keys', t => {
    const { update } = createPersistoid(config)
    update({ a: undefined, b: 1 })
    clock.tick(1);
    update({ a: undefined, b: undefined })
    clock.tick(1);
    t.true(spy.calledTwice);
    t.true(spy.withArgs('persist:persist-reducer-test', '{"b":"1"}').calledOnce);
    t.true(spy.withArgs('persist:persist-reducer-test', '{}').calledOnce);
})


================================================
FILE: tests/flush.spec.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
import test from 'ava'
import { createStore } from 'redux'

import getStoredState from '../src/getStoredState'
import persistReducer from '../src/persistReducer'
import persistStore from '../src/persistStore'
import createMemoryStorage from './utils/createMemoryStorage'

const INCREMENT = 'INCREMENT'

interface StateObject {
  [key: string]: any;
}
const initialState: StateObject = { a: 0, b: 10, c: 100}
const reducer = (state = initialState, { type }: { type: any }) => {
  console.log('action', type)
  if (type === INCREMENT) {
    const result = state
    Object.keys(state).forEach((key) => {
      result[key] = state[key] + 1
    })
    return result
  }
  return state
}

const memoryStorage = createMemoryStorage()

const config = {
  key: 'persist-reducer-test',
  version: 1,
  storage: memoryStorage,
  debug: true,
  throttle: 1000,
}

test('state before flush is not updated, after flush is', t => {
  return new Promise((resolve) => {
    const rootReducer = persistReducer(config, reducer)
    const store = createStore(rootReducer)
    const persistor = persistStore(store, {}, async () => {
      store.dispatch({ type: INCREMENT })
      const state = store.getState()
      const storedPreFlush = await getStoredState(config)
      t.not(storedPreFlush && storedPreFlush.c, state.c)
      await persistor.flush()
      const storedPostFlush = await getStoredState(config)
      resolve(t.is(storedPostFlush && storedPostFlush.c, state.c))
    })
  })
})


================================================
FILE: tests/persistCombineReducers.spec.ts
================================================
import persistCombineReducers from '../src/persistCombineReducers'
import createMemoryStorage from './utils/createMemoryStorage'

import test from 'ava'

const config = {
  key: 'TestConfig',
  storage: createMemoryStorage()
}

test('persistCombineReducers returns a function', t => {
  const reducer = persistCombineReducers(config, {
    foo: () => ({})
  })

  t.is(typeof reducer, 'function')
})

/*
test.skip('persistCombineReducers merges two levels deep of state', t => {
  
})
*/


================================================
FILE: tests/persistReducer.spec.ts
================================================
import test from 'ava'
import sinon from 'sinon'

import persistReducer from '../src/persistReducer'
import createMemoryStorage from './utils/createMemoryStorage'
import { PERSIST } from '../src/constants'
import sleep from './utils/sleep'

const reducer = () => ({})
const config = {
  key: 'persist-reducer-test',
  version: 1,
  storage: createMemoryStorage()
}

test('persistedReducer does not automatically set _persist state', t => {
  const persistedReducer = persistReducer(config, reducer)
  const state = persistedReducer({}, {type: "UNDEFINED"})
  console.log('state', state)
  t.is(undefined, state._persist)
})

test('persistedReducer does returns versioned, rehydrate tracked _persist state upon PERSIST', t => {
  const persistedReducer = persistReducer(config, reducer)
  const register = sinon.spy()
  const rehydrate = sinon.spy()
  const state = persistedReducer({}, { type: PERSIST, register, rehydrate })
  t.deepEqual({ version: 1, rehydrated: false}, state._persist)
})

test('persistedReducer calls register and rehydrate after PERSIST', async (t) => {
  const persistedReducer = persistReducer(config, reducer)
  const register = sinon.spy()
  const rehydrate = sinon.spy()
  persistedReducer({}, { type: PERSIST, register, rehydrate })
  await sleep(5000)
  t.is(register.callCount, 1)
  t.is(rehydrate.callCount, 1)
})


================================================
FILE: tests/persistStore.spec.ts
================================================
import test from 'ava'
import sinon from 'sinon'

import configureStore from 'redux-mock-store'

import persistStore from '../src/persistStore'
import { PERSIST, REHYDRATE } from '../src/constants'
import find from './utils/find'

const mockStore = configureStore([])

test('persistStore dispatches PERSIST action', t => {
  const store = mockStore()
  persistStore(store)
  const actions = store.getActions()
  const persistAction = find(actions, { type: PERSIST })
  t.truthy(persistAction)
})

test('register method adds a key to the registry', t => {
  const store = mockStore()
  const persistor = persistStore(store)
  const actions = store.getActions()
  const persistAction = find(actions, { type: PERSIST })
  persistAction.register('canary')
  t.deepEqual(persistor.getState().registry, ['canary'])
})

test('rehydrate method fires with the expected shape', t => {
  const store = mockStore()
  persistStore(store)
  const actions = store.getActions()
  const persistAction = find(actions, { type: PERSIST })
  persistAction.rehydrate('canary', { foo: 'bar' }, null)
  const rehydrateAction = find(actions, { type: REHYDRATE })
  t.deepEqual(rehydrateAction, { type: REHYDRATE, key: 'canary', payload: { foo: 'bar' }, err: null })
})

test('rehydrate method removes provided key from registry', t => {
  const store = mockStore()
  const persistor = persistStore(store)
  const actions = store.getActions()
  const persistAction = find(actions, { type: PERSIST })

  // register canary
  persistAction.register('canary')
  t.deepEqual(persistor.getState().registry, ['canary'])

  // rehydrate canary
  persistAction.rehydrate('canary', { foo: 'bar' }, null)
  t.deepEqual(persistor.getState().registry, [])
})

test('rehydrate method removes exactly one of provided key from registry', t => {
  const store = mockStore()
  const persistor = persistStore(store)
  const actions = store.getActions()
  const persistAction = find(actions, { type: PERSIST })

  // register canary twice
  persistAction.register('canary')
  persistAction.register('canary')
  t.deepEqual(persistor.getState().registry, ['canary', 'canary'])

  // rehydrate canary
  persistAction.rehydrate('canary', { foo: 'bar' }, null)
  t.deepEqual(persistor.getState().registry, ['canary'])
})

test('once registry is cleared for first time, persistor is flagged as bootstrapped', t => {
  const store = mockStore()
  const persistor = persistStore(store)
  const actions = store.getActions()
  const persistAction = find(actions, { type: PERSIST })

  persistAction.register('canary')
  t.false(persistor.getState().bootstrapped)
  persistAction.rehydrate('canary', { foo: 'bar' }, null)
  t.true(persistor.getState().bootstrapped)
})

test('once persistor is flagged as bootstrapped, further registry changes do not affect this value', t => {
  const store = mockStore()
  const persistor = persistStore(store)
  const actions = store.getActions()
  const persistAction = find(actions, { type: PERSIST })

  persistAction.register('canary')
  t.false(persistor.getState().bootstrapped)
  persistAction.rehydrate('canary', { foo: 'bar' }, null)
  t.true(persistor.getState().bootstrapped)

  // add canary back, registry is updated but bootstrapped remains true
  persistAction.register('canary')
  t.deepEqual(persistor.getState().registry, ['canary'])
  t.true(persistor.getState().bootstrapped)
})

test('persistStore calls bootstrapped callback (at most once) if provided', t => {
  const store = mockStore()
  const bootstrappedCb = sinon.spy()  
  persistStore(store, {}, bootstrappedCb)
  const actions = store.getActions()
  const persistAction = find(actions, { type: PERSIST })
  
  persistAction.register('canary')
  persistAction.rehydrate('canary', { foo: 'bar' }, null)
  t.is(bootstrappedCb.callCount, 1)

  // further rehydrates do not trigger the cb
  persistAction.register('canary')
  persistAction.rehydrate('canary', { foo: 'bar' }, null)
  t.is(bootstrappedCb.callCount, 1)
})


================================================
FILE: tests/utils/brokenStorage.ts
================================================
/* eslint-disable @typescript-eslint/no-empty-function */
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/ban-types */
export default {
  getItem(): Promise<void> {
    return new Promise((resolve: Function, reject: Function) => {})
  },
  setItem(): Promise<void> {
    return new Promise((resolve: Function, reject: Function) => {})
  },
  removeItem(): Promise<void> {
    return new Promise((resolve: Function, reject: Function) => {})
  },
  getAllKeys(): Promise<void> {
    return new Promise((resolve: Function, reject: Function) => {})
  },
  keys: []
}


================================================
FILE: tests/utils/createMemoryStorage.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Storage } from "../../src/types"

interface StateObj {
  [key: string]: any;
}

export function createMemoryStorage():Storage {
  const state: StateObj = {}
  return {
    getItem(key: string): Promise<string> {
      return Promise.resolve(state[key])
    },
    setItem(key: string, value: any): Promise<void> {
      state[key] = value
      return Promise.resolve(value)
    },
    removeItem(key: string): Promise<void> {
      delete state[key]
      return Promise.resolve()
    },
    getAllKeys(): Promise<Array<string>> {
      return Promise.resolve(Object.keys(state))
    },
    keys: Object.keys(state)
  }
}

export default createMemoryStorage


================================================
FILE: tests/utils/find.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
export default (collection: Array<Record<string, any>>, predicate: Record<string, string>): any => {
  let result = {}
  collection.forEach((value: any) => {
    if (value.type && value.type === predicate.type) {
      result = value
    }
  })
  return result
}


================================================
FILE: tests/utils/sleep.ts
================================================
export default function (timeout: number): Promise<void> {
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  return new Promise((resolve, _) => {
    setTimeout(resolve, timeout)
  })
}


================================================
FILE: tsconfig.json
================================================
{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */

    /* Basic Options */
    // "incremental": true,                         /* Enable incremental compilation */
    "target": "es2015",                             /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
    "module": "commonjs",                           /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
    // "lib": [],                                   /* Specify library files to be included in the compilation. */
    // "allowJs": true,                             /* Allow javascript files to be compiled. */
    // "checkJs": true,                             /* Report errors in .js files. */
    // "jsx": "preserve",                           /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
    "declaration": true,                            /* Generates corresponding '.d.ts' file. */
    // "declarationMap": true,                      /* Generates a sourcemap for each corresponding '.d.ts' file. */
    // "sourceMap": true,                           /* Generates corresponding '.map' file. */
    // "outFile": "./",                             /* Concatenate and emit output to single file. */
    "outDir": "./dist",                             /* Redirect output structure to the directory. */
    // "rootDir": "./",                             /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
    // "composite": true,                           /* Enable project compilation */
    // "tsBuildInfoFile": "./",                     /* Specify file to store incremental compilation information */
    // "removeComments": true,                      /* Do not emit comments to output. */
    // "noEmit": true,                              /* Do not emit outputs. */
    // "importHelpers": true,                       /* Import emit helpers from 'tslib'. */
    // "downlevelIteration": true,                  /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
    // "isolatedModules": true,                     /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */

    /* Strict Type-Checking Options */
    "strict": true,                                 /* Enable all strict type-checking options. */
    // "noImplicitAny": true,                       /* Raise error on expressions and declarations with an implied 'any' type. */
    // "strictNullChecks": true,                    /* Enable strict null checks. */
    // "strictFunctionTypes": true,                 /* Enable strict checking of function types. */
    // "strictBindCallApply": true,                 /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
    // "strictPropertyInitialization": true,        /* Enable strict checking of property initialization in classes. */
    // "noImplicitThis": true,                      /* Raise error on 'this' expressions with an implied 'any' type. */
    // "alwaysStrict": true,                        /* Parse in strict mode and emit "use strict" for each source file. */

    /* Additional Checks */
    // "noUnusedLocals": true,                      /* Report errors on unused locals. */
    // "noUnusedParameters": true,                  /* Report errors on unused parameters. */
    // "noImplicitReturns": true,                   /* Report error when not all code paths in function return a value. */
    // "noFallthroughCasesInSwitch": true,          /* Report errors for fallthrough cases in switch statement. */
    // "noUncheckedIndexedAccess": true,            /* Include 'undefined' in index signature results */
    // "noImplicitOverride": true,                  /* Ensure overriding members in derived classes are marked with an 'override' modifier. */
    // "noPropertyAccessFromIndexSignature": true,  /* Require undeclared properties from index signatures to use element accesses. */

    /* Module Resolution Options */
    "moduleResolution": "node",                     /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
    // "baseUrl": "./",                             /* Base directory to resolve non-absolute module names. */
    // "paths": {},                                 /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
    // "rootDirs": [],                              /* List of root folders whose combined content represents the structure of the project at runtime. */
    // "typeRoots": [],                             /* List of folders to include type definitions from. */
    // "types": [],                                 /* Type declaration files to be included in compilation. */
    // "allowSyntheticDefaultImports": true,        /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
    "esModuleInterop": true,                        /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    // "preserveSymlinks": true,                    /* Do not resolve the real path of symlinks. */
    // "allowUmdGlobalAccess": true,                /* Allow accessing UMD globals from modules. */

    /* Source Map Options */
    // "sourceRoot": "",                            /* Specify the location where debugger should locate TypeScript files instead of source locations. */
    // "mapRoot": "",                               /* Specify the location where debugger should locate map files instead of generated locations. */
    // "inlineSourceMap": true,                     /* Emit a single file with source maps instead of having a separate file. */
    // "inlineSources": true,                       /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */

    /* Experimental Options */
    // "experimentalDecorators": true,              /* Enables experimental support for ES7 decorators. */
    // "emitDecoratorMetadata": true,               /* Enables experimental support for emitting type metadata for decorators. */

    /* Advanced Options */
    "skipLibCheck": true,                           /* Skip type checking of declaration files. */
    "forceConsistentCasingInFileNames": true        /* Disallow inconsistently-cased references to the same file. */
  },
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules"
  ]
}
Download .txt
gitextract_um4gqsx8/

├── .babelrc.js
├── .eslintrc.yml
├── .github/
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── .prettierrc
├── CHANGELOG.md
├── LIBSIZE.md
├── LICENSE
├── README.md
├── docs/
│   ├── MigrationGuide-v5.md
│   ├── PersistGate.md
│   ├── api.md
│   ├── hot-module-replacement.md
│   ├── migrations.md
│   └── v5-migration-alternate.md
├── integration/
│   ├── README.md
│   └── react/
│       └── package.json
├── package.json
├── rollup.config.js
├── scripts/
│   └── size-estimator.js
├── src/
│   ├── constants.ts
│   ├── createMigrate.ts
│   ├── createPersistoid.ts
│   ├── createTransform.ts
│   ├── getStoredState.ts
│   ├── index.ts
│   ├── integration/
│   │   ├── getStoredStateMigrateV4.ts
│   │   └── react.ts
│   ├── persistCombineReducers.ts
│   ├── persistReducer.ts
│   ├── persistStore.ts
│   ├── purgeStoredState.ts
│   ├── stateReconciler/
│   │   ├── autoMergeLevel1.ts
│   │   ├── autoMergeLevel2.ts
│   │   └── hardSet.ts
│   ├── storage/
│   │   ├── createWebStorage.ts
│   │   ├── getStorage.ts
│   │   ├── index.ts
│   │   └── session.ts
│   └── types.ts
├── tests/
│   ├── complete.spec.ts
│   ├── createPersistor.spec.ts
│   ├── flush.spec.ts
│   ├── persistCombineReducers.spec.ts
│   ├── persistReducer.spec.ts
│   ├── persistStore.spec.ts
│   └── utils/
│       ├── brokenStorage.ts
│       ├── createMemoryStorage.ts
│       ├── find.ts
│       └── sleep.ts
└── tsconfig.json
Download .txt
SYMBOL INDEX (75 symbols across 20 files)

FILE: src/constants.ts
  constant KEY_PREFIX (line 1) | const KEY_PREFIX = 'persist:'
  constant FLUSH (line 2) | const FLUSH = 'persist/FLUSH'
  constant REHYDRATE (line 3) | const REHYDRATE = 'persist/REHYDRATE'
  constant PAUSE (line 4) | const PAUSE = 'persist/PAUSE'
  constant PERSIST (line 5) | const PERSIST = 'persist/PERSIST'
  constant PURGE (line 6) | const PURGE = 'persist/PURGE'
  constant REGISTER (line 7) | const REGISTER = 'persist/REGISTER'
  constant DEFAULT_VERSION (line 8) | const DEFAULT_VERSION = -1

FILE: src/createMigrate.ts
  function createMigrate (line 6) | function createMigrate(

FILE: src/createPersistoid.ts
  function createPersistoid (line 7) | function createPersistoid(config: PersistConfig<any>): Persistoid {
  function defaultSerialize (line 141) | function defaultSerialize(data: any) {

FILE: src/createTransform.ts
  type TransformConfig (line 2) | type TransformConfig = {
  function createTransform (line 7) | function createTransform(

FILE: src/getStoredState.ts
  function getStoredState (line 6) | function getStoredState(
  function defaultDeserialize (line 47) | function defaultDeserialize(serial: string) {

FILE: src/integration/getStoredStateMigrateV4.ts
  type V4Config (line 6) | type V4Config = {
  function getStoredState (line 15) | function getStoredState(v4Config: V4Config) {
  constant KEY_PREFIX (line 24) | const KEY_PREFIX = 'reduxPersist:'
  function hasLocalStorage (line 26) | function hasLocalStorage() {
  method getItem (line 73) | getItem(key: string, cb: any) {
  method setItem (line 81) | setItem(key: string, string: string, cb: any) {
  method removeItem (line 89) | removeItem(key: string, cb: any) {
  function getStoredStateV4 (line 101) | function getStoredStateV4(v4Config: V4Config) {

FILE: src/integration/react.ts
  type Props (line 5) | type Props = {
  type State (line 12) | type State = {
  class PersistGate (line 16) | class PersistGate extends PureComponent<Props, State> {
    method componentDidMount (line 27) | componentDidMount(): void {
    method componentWillUnmount (line 48) | componentWillUnmount(): void {
    method render (line 52) | render(): ReactNode {

FILE: src/persistCombineReducers.ts
  function persistCombineReducers (line 12) | function persistCombineReducers<S, A extends Action>(

FILE: src/persistReducer.ts
  type PersistPartial (line 26) | type PersistPartial = { _persist: PersistState } | any;
  constant DEFAULT_TIMEOUT (line 27) | const DEFAULT_TIMEOUT = 5000
  function persistReducer (line 33) | function persistReducer<S, A extends Action>(

FILE: src/persistStore.ts
  type BoostrappedCb (line 11) | type BoostrappedCb = () => any;
  type OptionToTestObject (line 32) | interface OptionToTestObject {
  function persistStore (line 36) | function persistStore(

FILE: src/purgeStoredState.ts
  function purgeStoredState (line 6) | function purgeStoredState(config: PersistConfig<any>):any {
  function warnIfRemoveError (line 14) | function warnIfRemoveError(err: any) {

FILE: src/stateReconciler/autoMergeLevel1.ts
  function autoMergeLevel1 (line 10) | function autoMergeLevel1<S extends KeyAccessState>(

FILE: src/stateReconciler/autoMergeLevel2.ts
  function autoMergeLevel2 (line 11) | function autoMergeLevel2<S extends KeyAccessState>(
  function isPlainEnoughObject (line 58) | function isPlainEnoughObject(o: unknown) {

FILE: src/stateReconciler/hardSet.ts
  function hardSet (line 6) | function hardSet<S>(inboundState: S): S {

FILE: src/storage/createWebStorage.ts
  function createWebStorage (line 4) | function createWebStorage(type: string): any {

FILE: src/storage/getStorage.ts
  function noop (line 4) | function noop() {}
  function hasStorage (line 13) | function hasStorage(storageType: string) {
  function getStorage (line 34) | function getStorage(type: string): Storage {

FILE: src/types.ts
  type PersistState (line 7) | interface PersistState {
  type PersistedState (line 12) | type PersistedState = {
  type PersistMigrate (line 16) | type PersistMigrate =
  type StateReconciler (line 19) | type StateReconciler<S> =
  type KeyAccessState (line 22) | interface KeyAccessState {
  type PersistConfig (line 33) | interface PersistConfig<S, RS = any, HSS = any, ESS = any> {
  type PersistorOptions (line 58) | interface PersistorOptions {
  type Storage (line 63) | interface Storage {
  type WebStorage (line 71) | interface WebStorage extends Storage {
  type MigrationManifest (line 86) | interface MigrationManifest {
  type TransformInbound (line 96) | type TransformInbound<SS, ESS, S = any> =
  type TransformOutbound (line 105) | type TransformOutbound<SS, HSS, RS = any> =
  type Transform (line 108) | interface Transform<HSS, ESS, S = any, RS = any> {
  type RehydrateErrorType (line 113) | type RehydrateErrorType = any;
  type RehydrateAction (line 115) | interface RehydrateAction {
  type Persistoid (line 122) | interface Persistoid {
  type RegisterAction (line 127) | interface RegisterAction {
  type PersistorAction (line 132) | type PersistorAction =
  type PersistorState (line 137) | interface PersistorState {
  type PersistorSubscribeCallback (line 142) | type PersistorSubscribeCallback = () => any;
  type Persistor (line 148) | interface Persistor {

FILE: tests/flush.spec.ts
  constant INCREMENT (line 10) | const INCREMENT = 'INCREMENT'
  type StateObject (line 12) | interface StateObject {

FILE: tests/utils/brokenStorage.ts
  method getItem (line 5) | getItem(): Promise<void> {
  method setItem (line 8) | setItem(): Promise<void> {
  method removeItem (line 11) | removeItem(): Promise<void> {
  method getAllKeys (line 14) | getAllKeys(): Promise<void> {

FILE: tests/utils/createMemoryStorage.ts
  type StateObj (line 4) | interface StateObj {
  function createMemoryStorage (line 8) | function createMemoryStorage():Storage {
Condensed preview — 51 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (99K chars).
[
  {
    "path": ".babelrc.js",
    "chars": 110,
    "preview": "/**\n * Babel Configuration\n */\n module.exports = {\n  presets: [\n    [\n      \"@babel/preset-env\"\n    ],\n  ],\n}\n"
  },
  {
    "path": ".eslintrc.yml",
    "chars": 224,
    "preview": "---\n\nparser: '@typescript-eslint/parser'\n\nglobals:\n  it: true\n  expect: true\n  describe: true\n  test: true\n  jest: true\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 580,
    "preview": "name: redux-persist ci\non: [push, pull_request]\n\njobs:\n  # Label of the container job\n  container-job:\n    # Containers "
  },
  {
    "path": ".gitignore",
    "chars": 199,
    "preview": "es\nlib\ndist\ntypes\n.watchmanconfig\n\n# Logs\nlogs\n*.log\nnpm-debug.log*\n\n# Dependency directories\nnode_modules\n\n# Optional n"
  },
  {
    "path": ".prettierrc",
    "chars": 70,
    "preview": "parser: \"typescript\"\nsemi: false\ntrailingComma: es5\nsingleQuote: true\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 640,
    "preview": "# Changelog\nAll notable changes to this project (after v6.1.0) should be documented in this file.\n\nThe format is (mostly"
  },
  {
    "path": "LIBSIZE.md",
    "chars": 699,
    "preview": "### Redux Persist Size Estimate\nThe following is a history of size estimates in bytes. This is calculated as a rollup mi"
  },
  {
    "path": "LICENSE",
    "chars": 1067,
    "preview": "MIT License\n\nCopyright (c) 2017 Zack Story\n\nPermission is hereby granted, free of charge, to any person obtaining a copy"
  },
  {
    "path": "README.md",
    "chars": 17222,
    "preview": "# Redux Persist\nPersist and rehydrate a redux store.\n\n[![build status](https://img.shields.io/travis/rt2zz/redux-persist"
  },
  {
    "path": "docs/MigrationGuide-v5.md",
    "chars": 3765,
    "preview": "## v5 Breaking Changes\nThere are three important breaking changes.\n1. api has changed as described in the [migration](#m"
  },
  {
    "path": "docs/PersistGate.md",
    "chars": 745,
    "preview": "`PersistGate` delays the rendering of your app's UI until your persisted state has been retrieved and saved to redux.\n\n*"
  },
  {
    "path": "docs/api.md",
    "chars": 3770,
    "preview": "# Redux Persist API\n---\n## Standard API\n- [persistReducer](#persistreducerconfig-reducer)([config](#type-persistconfig),"
  },
  {
    "path": "docs/hot-module-replacement.md",
    "chars": 897,
    "preview": "## Hot Module Replacement\n\nHot Module Replacement (HMR) is a wonderful feature that is really useful in development envi"
  },
  {
    "path": "docs/migrations.md",
    "chars": 1075,
    "preview": "# Redux Persist Migration Example\n\n### Example with createMigrate\n```js\nimport { createMigrate, persistReducer, persistS"
  },
  {
    "path": "docs/v5-migration-alternate.md",
    "chars": 987,
    "preview": "## Alternate Migration\nIf in redux-persist you used getStoredState + createPersistor, the v5 usage is similar with some "
  },
  {
    "path": "integration/README.md",
    "chars": 95,
    "preview": "Proxy package to enable\n```js\nimport { PersistGate } from 'redux-persist/integration/react'\n```"
  },
  {
    "path": "integration/react/package.json",
    "chars": 209,
    "preview": "{\n    \"name\": \"redux-persist/integration/react\",\n    \"private\": true,\n    \"main\": \"../../lib/integration/react\",\n    \"mo"
  },
  {
    "path": "package.json",
    "chars": 2134,
    "preview": "{\n  \"name\": \"redux-persist\",\n  \"version\": \"6.1.0\",\n  \"description\": \"persist and rehydrate redux stores\",\n  \"main\": \"lib"
  },
  {
    "path": "rollup.config.js",
    "chars": 1420,
    "preview": "import pluginNodeResolve from \"@rollup/plugin-node-resolve\"\nimport pluginCommonjs from \"@rollup/plugin-commonjs\"\nimport "
  },
  {
    "path": "scripts/size-estimator.js",
    "chars": 726,
    "preview": "const { execSync }  = require('child_process')\nconst packageJson = require('../package.json')\n\nlet packageVersion = pack"
  },
  {
    "path": "src/constants.ts",
    "chars": 311,
    "preview": "export const KEY_PREFIX = 'persist:'\nexport const FLUSH = 'persist/FLUSH'\nexport const REHYDRATE = 'persist/REHYDRATE'\ne"
  },
  {
    "path": "src/createMigrate.ts",
    "chars": 2014,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { DEFAULT_VERSION } from './constants'\n\nimport type { Per"
  },
  {
    "path": "src/createPersistoid.ts",
    "chars": 4155,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { KEY_PREFIX } from './constants'\n\nimport type { Persisto"
  },
  {
    "path": "src/createTransform.ts",
    "chars": 1284,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\ntype TransformConfig = {\n  whitelist?: Array<string>,\n  blacklis"
  },
  {
    "path": "src/getStoredState.ts",
    "chars": 1522,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { KeyAccessState, PersistConfig } from './types'\n\nim"
  },
  {
    "path": "src/index.ts",
    "chars": 537,
    "preview": "export { default as persistReducer } from './persistReducer'\nexport { default as persistCombineReducers } from './persis"
  },
  {
    "path": "src/integration/getStoredStateMigrateV4.ts",
    "chars": 5074,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport getStoredStateV5 from '../getStoredState'\n\nimport type { "
  },
  {
    "path": "src/integration/react.ts",
    "chars": 1755,
    "preview": "// eslint-disable-next-line @typescript-eslint/no-unused-vars\nimport React, { PureComponent, ReactNode } from 'react'\nim"
  },
  {
    "path": "src/persistCombineReducers.ts",
    "chars": 839,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Action, AnyAction, CombinedState, combineReducers, Redu"
  },
  {
    "path": "src/persistReducer.ts",
    "chars": 6605,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n  Action, AnyAction, Reducer\n} from 'redux'\n\nimport {\n "
  },
  {
    "path": "src/persistStore.ts",
    "chars": 3170,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type {\n  Persistor,\n  PersistorOptions,\n  PersistorState,"
  },
  {
    "path": "src/purgeStoredState.ts",
    "chars": 631,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { PersistConfig } from './types'\n\nimport { KEY_PREFI"
  },
  {
    "path": "src/stateReconciler/autoMergeLevel1.ts",
    "chars": 1373,
    "preview": "/*\n  autoMergeLevel1: \n    - merges 1 level of substate\n    - skips substate if already modified\n*/\n\nimport type { Persi"
  },
  {
    "path": "src/stateReconciler/autoMergeLevel2.ts",
    "chars": 1747,
    "preview": "/*\n  autoMergeLevel2: \n    - merges 2 level of substate\n    - skips substate if already modified\n    - this is essential"
  },
  {
    "path": "src/stateReconciler/hardSet.ts",
    "chars": 130,
    "preview": "/*\n  hardSet: \n    - hard set incoming state\n*/\n\nexport default function hardSet<S>(inboundState: S): S {\n  return inbou"
  },
  {
    "path": "src/storage/createWebStorage.ts",
    "chars": 667,
    "preview": "import getStorage from './getStorage'\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport default fun"
  },
  {
    "path": "src/storage/getStorage.ts",
    "chars": 1241,
    "preview": "import type { Storage } from '../types'\n\n// eslint-disable-next-line @typescript-eslint/no-empty-function\nfunction noop("
  },
  {
    "path": "src/storage/index.ts",
    "chars": 92,
    "preview": "import createWebStorage from './createWebStorage'\n\nexport default createWebStorage('local')\n"
  },
  {
    "path": "src/storage/session.ts",
    "chars": 94,
    "preview": "import createWebStorage from './createWebStorage'\n\nexport default createWebStorage('session')\n"
  },
  {
    "path": "src/types.ts",
    "chars": 3779,
    "preview": "/* eslint-disable @typescript-eslint/ban-types */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { REHYD"
  },
  {
    "path": "tests/complete.spec.ts",
    "chars": 1831,
    "preview": "import test from 'ava'\nimport { combineReducers, createStore } from 'redux'\n\nimport persistReducer from '../src/persistR"
  },
  {
    "path": "tests/createPersistor.spec.ts",
    "chars": 1735,
    "preview": "import test from 'ava'\nimport sinon from 'sinon'\nimport createMemoryStorage from './utils/createMemoryStorage'\nimport cr"
  },
  {
    "path": "tests/flush.spec.ts",
    "chars": 1534,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport test from 'ava'\nimport { createStore } from 'redux'\n\nimpo"
  },
  {
    "path": "tests/persistCombineReducers.spec.ts",
    "chars": 488,
    "preview": "import persistCombineReducers from '../src/persistCombineReducers'\nimport createMemoryStorage from './utils/createMemory"
  },
  {
    "path": "tests/persistReducer.spec.ts",
    "chars": 1346,
    "preview": "import test from 'ava'\nimport sinon from 'sinon'\n\nimport persistReducer from '../src/persistReducer'\nimport createMemory"
  },
  {
    "path": "tests/persistStore.spec.ts",
    "chars": 3978,
    "preview": "import test from 'ava'\nimport sinon from 'sinon'\n\nimport configureStore from 'redux-mock-store'\n\nimport persistStore fro"
  },
  {
    "path": "tests/utils/brokenStorage.ts",
    "chars": 607,
    "preview": "/* eslint-disable @typescript-eslint/no-empty-function */\n/* eslint-disable @typescript-eslint/no-unused-vars */\n/* esli"
  },
  {
    "path": "tests/utils/createMemoryStorage.ts",
    "chars": 724,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Storage } from \"../../src/types\"\n\ninterface StateObj {\n"
  },
  {
    "path": "tests/utils/find.ts",
    "chars": 319,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nexport default (collection: Array<Record<string, any>>, predicat"
  },
  {
    "path": "tests/utils/sleep.ts",
    "chars": 202,
    "preview": "export default function (timeout: number): Promise<void> {\n  // eslint-disable-next-line @typescript-eslint/no-unused-va"
  },
  {
    "path": "tsconfig.json",
    "chars": 6866,
    "preview": "{\n  \"compilerOptions\": {\n    /* Visit https://aka.ms/tsconfig.json to read more about this file */\n\n    /* Basic Options"
  }
]

About this extraction

This page contains the full source code of the rt2zz/redux-persist GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 51 files (91.1 KB), approximately 23.8k tokens, and a symbol index with 75 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!