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

Code before:
      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

  // The state that was loaded from the storage and is set via set() within this
  // middleware would later be overridden with the initial state by create(). To
  // avoid this we merge the state from localStorage into the initial state. This
  // way the state gets overridden with the state loaded from storage.
  // TODO: find a better solution for this
  let stateFromStorage
  ;(() => {
    const postRehydrationCallback = onRehydrateStorage?.(get()) || undefined
    // bind is used to avoid `TypeError: Illegal invocation` error
    let storageValuePending = toThenable(storage.getItem.bind(storage))(name)
    storageValuePending
      .then((storageValue) => {
        if (storageValue) {
          return deserialize(storageValue)
        }
      })
      .then((deserializedStorageValue) => {
        if (deserializedStorageValue) {
          if (deserializedStorageValue.version !== version) {
            if (!migrate) return
            stateFromStorage = migrate(
              deserializedStorageValue.state,
              deserializedStorageValue.version
            )
            return stateFromStorage
          } else {
            stateFromStorage = deserializedStorageValue.state
            set(deserializedStorageValue.state)
          }
        }
      })
      .then((migratedState) => {
        if (migratedState) {
          set(migratedState as PartialState<S, keyof S>)
          return setItem()
        }

Review comment (suggestion):
```suggestion
    toThenable(storage.getItem.bind(storage))(name)
```

Code after:

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

    let errorInSync: Error | undefined
    const thenable = thenableSerialize({ state, version })
      .then((serializedValue) =>
        (storage as StateStorage).setItem(name, serializedValue)
      )
      .catch((e) => {
        errorInSync = e
      })
    if (errorInSync) {
      throw errorInSync
    }
    return thenable
  }

  const savedSetState = api.setState

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

  // rehydrate initial state with existing stored state

  // a workaround to solve the issue of not storing rehydrated state in sync storage
  // the set(state) value would be later overridden with initial state by create()
  // to avoid this, we merge the state from localStorage into the initial state.
  let stateFromStorageInSync: S | undefined
  const postRehydrationCallback = onRehydrateStorage?.(get()) || undefined
  // bind is used to avoid `TypeError: Illegal invocation` error
  toThenable(storage.getItem.bind(storage))(name)
    .then((storageValue) => {
      if (storageValue) {
        return deserialize(storageValue)
      }
    })
    .then((deserializedStorageValue) => {
      if (deserializedStorageValue) {
        if (deserializedStorageValue.version !== version) {
          if (migrate) {
            return migrate(
              deserializedStorageValue.state,