Repository: pmndrs/zustand
File: src/middleware.ts (TypeScript)

Code before:
          `Persist middleware: unable to update ${name}, the given storage is currently unavailable.`
        )
        set(...args)
      },
      get,
      api
    )
  }

  const setItem = (): Promise<void> | void => {
    const state = { ...get() }

    if (whitelist) {
      ;(Object.keys(state) as (keyof S)[]).forEach((key) => {
        !whitelist.includes(key) && delete state[key]
      })
    }
    if (blacklist) {
      blacklist.forEach((key) => delete state[key])
    }

    if (storage) {
      const serializeResult = serialize({ state, version })
      return serializeResult instanceof Promise
        ? serializeResult.then((value) => storage.setItem(name, value))
        : storage.setItem(name, serializeResult)
    }
  }

  const savedSetState = api.setState

  api.setState = (state, replace) => {
    savedSetState(state, replace)
    void setItem()
  }

  // rehydrate initial state with existing stored state
  ;(() => {
    const postRehydrationCallback = onRehydrateStorage?.(get()) || undefined

    let storageValueUnknown = storage.getItem(name)

    if (storageValueUnknown instanceof Promise) {
      storageValueUnknown
        .then((storageValue) => storageValue && deserialize(storageValue))
        .then((deserializedStorageValue) => {
          if (
            deserializedStorageValue &&
            deserializedStorageValue.version !== version
          ) {
            return migrate?.(

Review comment (suggestion):
I think your idea of `toThenable()` is super neat.
```js
const toThenable = (fn, input) => {
  try {
    const result = fn(input)
    if (result instanceof Promise) {
      return result
    }
    return { then: (onFulfilled) => onFulfilled(result) }
  } catch (e) {
    return { then: (onFulfilled, onRejected) => onRejected(e) }
  }
}
```

```suggestion
      toThenable(serialize, { state, version }).then(
        (value) => storage.setItem(name, value),
        (err) => { throw e }
      )
```

Something like this? @TheEhsanSarshar 

Code after:
      },
    }
  } catch (e) {
    return {
      then(_onFulfilled) {
        return this as Thenable<any>
      },
      catch(onRejected) {
        return toThenable(onRejected)(e)
      },
    }
  }
}

export const persist = <S extends State>(
  config: StateCreator<S>,
  options: PersistOptions<S>
) => (set: SetState<S>, get: GetState<S>, api: StoreApi<S>): S => {
  const {
    name,
    getStorage = () => localStorage,
    serialize = JSON.stringify as (state: StorageValue<S>) => string,
    deserialize = JSON.parse as (str: string) => StorageValue<S>,
    blacklist,
    whitelist,
    onRehydrateStorage,
    version = 0,
    migrate,
  } = options || {}

  let storage: StateStorage | undefined

  try {
    storage = getStorage()
  } catch (e) {
    // prevent error if the storage is not defined (e.g. when server side rendering a page)
  }

  if (!storage) {
    return config(
      (...args) => {
        console.warn(
          `Persist middleware: unable to update ${name}, the given storage is currently unavailable.`
        )
        set(...args)
      },
      get,
      api
    )
  }