You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							142 lines
						
					
					
						
							3.9 KiB
						
					
					
				
			
		
		
	
	
							142 lines
						
					
					
						
							3.9 KiB
						
					
					
				// @flow | 
						|
 | 
						|
import { KEY_PREFIX, REHYDRATE } from './constants' | 
						|
 | 
						|
import type { Persistoid, PersistConfig, Transform } from './types' | 
						|
 | 
						|
type IntervalID = any // @TODO remove once flow < 0.63 support is no longer required. | 
						|
 | 
						|
export default function createPersistoid(config: PersistConfig): Persistoid { | 
						|
  // defaults | 
						|
  const blacklist: ?Array<string> = config.blacklist || null | 
						|
  const whitelist: ?Array<string> = 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 | 
						|
  if (config.serialize === false) { | 
						|
    serialize = x => x | 
						|
  } else if (typeof config.serialize === 'function') { | 
						|
    serialize = config.serialize | 
						|
  } else { | 
						|
    serialize = defaultSerialize | 
						|
  } | 
						|
  const writeFailHandler = config.writeFailHandler || null | 
						|
 | 
						|
  // initialize stateful values | 
						|
  let lastState = {} | 
						|
  let stagedState = {} | 
						|
  let keysToProcess = [] | 
						|
  let timeIterator: ?IntervalID = null | 
						|
  let writePromise = null | 
						|
 | 
						|
  const update = (state: Object) => { | 
						|
    // 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 | 
						|
    } | 
						|
 | 
						|
    let key = keysToProcess.shift() | 
						|
    let 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) { | 
						|
    if (whitelist && whitelist.indexOf(key) === -1 && key !== '_persist') | 
						|
      return false | 
						|
    if (blacklist && blacklist.indexOf(key) !== -1) return false | 
						|
    return true | 
						|
  } | 
						|
 | 
						|
  function onWriteFail(err) { | 
						|
    // @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) { | 
						|
  return JSON.stringify(data) | 
						|
}
 | 
						|
 |