/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import { checkValidArgs, checkValidInput, convertError, convertErrors } from './helpers';
import RCTAsyncStorage from './RCTAsyncStorage';

if (!RCTAsyncStorage) {
  throw new Error(`[@RNC/AsyncStorage]: NativeModule: AsyncStorage is null.

To fix this issue try these steps:

  • Rebuild and restart the app.

  • Run the packager with \`--reset-cache\` flag.

  • If you are using CocoaPods on iOS, run \`pod install\` in the \`ios\` directory and then rebuild and re-run the app.

  • If this happens while testing with Jest, check out docs how to integrate AsyncStorage with it: https://react-native-async-storage.github.io/async-storage/docs/advanced/jest

If none of these fix the issue, please open an issue on the Github repository: https://github.com/react-native-async-storage/async-storage/issues
`);
}
/**
 * `AsyncStorage` is a simple, unencrypted, asynchronous, persistent, key-value
 * storage system that is global to the app. It should be used instead of
 * LocalStorage.
 *
 * See https://react-native-async-storage.github.io/async-storage/docs/api
 */


const AsyncStorage = (() => {
  let _getRequests = [];
  let _getKeys = [];
  let _immediate = null;
  return {
    /**
     * Fetches an item for a `key` and invokes a callback upon completion.
     *
     * See https://react-native-async-storage.github.io/async-storage/docs/api#getitem
     */
    getItem: (key, callback) => {
      return new Promise((resolve, reject) => {
        checkValidInput(key);
        RCTAsyncStorage.multiGet([key], (errors, result) => {
          var _result$;

          // Unpack result to get value from [[key,value]]
          const value = result !== null && result !== void 0 && (_result$ = result[0]) !== null && _result$ !== void 0 && _result$[1] ? result[0][1] : null;
          const errs = convertErrors(errors);
          callback === null || callback === void 0 ? void 0 : callback(errs === null || errs === void 0 ? void 0 : errs[0], value);

          if (errs) {
            reject(errs[0]);
          } else {
            resolve(value);
          }
        });
      });
    },

    /**
     * Sets the value for a `key` and invokes a callback upon completion.
     *
     * See https://react-native-async-storage.github.io/async-storage/docs/api#setitem
     */
    setItem: (key, value, callback) => {
      return new Promise((resolve, reject) => {
        checkValidInput(key, value);
        RCTAsyncStorage.multiSet([[key, value]], errors => {
          const errs = convertErrors(errors);
          callback === null || callback === void 0 ? void 0 : callback(errs === null || errs === void 0 ? void 0 : errs[0]);

          if (errs) {
            reject(errs[0]);
          } else {
            resolve();
          }
        });
      });
    },

    /**
     * Removes an item for a `key` and invokes a callback upon completion.
     *
     * See https://react-native-async-storage.github.io/async-storage/docs/api#removeitem
     */
    removeItem: (key, callback) => {
      return new Promise((resolve, reject) => {
        checkValidInput(key);
        RCTAsyncStorage.multiRemove([key], errors => {
          const errs = convertErrors(errors);
          callback === null || callback === void 0 ? void 0 : callback(errs === null || errs === void 0 ? void 0 : errs[0]);

          if (errs) {
            reject(errs[0]);
          } else {
            resolve();
          }
        });
      });
    },

    /**
     * Merges an existing `key` value with an input value, assuming both values
     * are stringified JSON.
     *
     * **NOTE:** This is not supported by all native implementations.
     *
     * See https://react-native-async-storage.github.io/async-storage/docs/api#mergeitem
     */
    mergeItem: (key, value, callback) => {
      return new Promise((resolve, reject) => {
        checkValidInput(key, value);
        RCTAsyncStorage.multiMerge([[key, value]], errors => {
          const errs = convertErrors(errors);
          callback === null || callback === void 0 ? void 0 : callback(errs === null || errs === void 0 ? void 0 : errs[0]);

          if (errs) {
            reject(errs[0]);
          } else {
            resolve();
          }
        });
      });
    },

    /**
     * Erases *all* `AsyncStorage` for all clients, libraries, etc. You probably
     * don't want to call this; use `removeItem` or `multiRemove` to clear only
     * your app's keys.
     *
     * See https://react-native-async-storage.github.io/async-storage/docs/api#clear
     */
    clear: callback => {
      return new Promise((resolve, reject) => {
        RCTAsyncStorage.clear(error => {
          const err = convertError(error);
          callback === null || callback === void 0 ? void 0 : callback(err);

          if (err) {
            reject(err);
          } else {
            resolve();
          }
        });
      });
    },

    /**
     * Gets *all* keys known to your app; for all callers, libraries, etc.
     *
     * See https://react-native-async-storage.github.io/async-storage/docs/api#getallkeys
     */
    getAllKeys: callback => {
      return new Promise((resolve, reject) => {
        RCTAsyncStorage.getAllKeys((error, keys) => {
          const err = convertError(error);
          callback === null || callback === void 0 ? void 0 : callback(err, keys);

          if (keys) {
            resolve(keys);
          } else {
            reject(err);
          }
        });
      });
    },

    /**
     * The following batched functions are useful for executing a lot of
     * operations at once, allowing for native optimizations and provide the
     * convenience of a single callback after all operations are complete.
     *
     * These functions return arrays of errors, potentially one for every key.
     * For key-specific errors, the Error object will have a key property to
     * indicate which key caused the error.
     */

    /**
     * Flushes any pending requests using a single batch call to get the data.
     *
     * See https://react-native-async-storage.github.io/async-storage/docs/api#flushgetrequests
     * */
    flushGetRequests: () => {
      const getRequests = _getRequests;
      const getKeys = _getKeys;
      _getRequests = [];
      _getKeys = [];
      RCTAsyncStorage.multiGet(getKeys, (errors, result) => {
        // Even though the runtime complexity of this is theoretically worse vs if we used a map,
        // it's much, much faster in practice for the data sets we deal with (we avoid
        // allocating result pair arrays). This was heavily benchmarked.
        //
        // Is there a way to avoid using the map but fix the bug in this breaking test?
        // https://github.com/facebook/react-native/commit/8dd8ad76579d7feef34c014d387bf02065692264
        const map = {};
        result === null || result === void 0 ? void 0 : result.forEach(_ref => {
          let [key, value] = _ref;
          map[key] = value;
          return value;
        });
        const reqLength = getRequests.length;
        /**
         * As mentioned few lines above, this method could be called with the array of potential error,
         * in case of anything goes wrong. The problem is, if any of the batched calls fails
         * the rest of them would fail too, but the error would be consumed by just one. The rest
         * would simply return `undefined` as their result, rendering false negatives.
         *
         * In order to avoid this situation, in case of any call failing,
         * the rest of them will be rejected as well (with the same error).
         */

        const errorList = convertErrors(errors);
        const error = errorList !== null && errorList !== void 0 && errorList.length ? errorList[0] : null;

        for (let i = 0; i < reqLength; i++) {
          var _request$callback2, _request$resolve;

          const request = getRequests[i];

          if (error) {
            var _request$callback, _request$reject;

            (_request$callback = request.callback) === null || _request$callback === void 0 ? void 0 : _request$callback.call(request, errorList);
            (_request$reject = request.reject) === null || _request$reject === void 0 ? void 0 : _request$reject.call(request, error);
            continue;
          }

          const requestResult = request.keys.map(key => [key, map[key]]);
          (_request$callback2 = request.callback) === null || _request$callback2 === void 0 ? void 0 : _request$callback2.call(request, null, requestResult);
          (_request$resolve = request.resolve) === null || _request$resolve === void 0 ? void 0 : _request$resolve.call(request, requestResult);
        }
      });
    },

    /**
     * This allows you to batch the fetching of items given an array of `key`
     * inputs. Your callback will be invoked with an array of corresponding
     * key-value pairs found.
     *
     * See https://react-native-async-storage.github.io/async-storage/docs/api#multiget
     */
    multiGet: (keys, callback) => {
      if (!_immediate) {
        _immediate = setImmediate(() => {
          _immediate = null;
          AsyncStorage.flushGetRequests();
        });
      }

      const getRequest = {
        keys: keys,
        callback: callback,
        // do we need this?
        keyIndex: _getKeys.length,
        resolve: null,
        reject: null
      };
      const promiseResult = new Promise((resolve, reject) => {
        getRequest.resolve = resolve;
        getRequest.reject = reject;
      });

      _getRequests.push(getRequest); // avoid fetching duplicates


      keys.forEach(key => {
        if (_getKeys.indexOf(key) === -1) {
          _getKeys.push(key);
        }
      });
      return promiseResult;
    },

    /**
     * Use this as a batch operation for storing multiple key-value pairs. When
     * the operation completes you'll get a single callback with any errors.
     *
     * See https://react-native-async-storage.github.io/async-storage/docs/api#multiset
     */
    multiSet: (keyValuePairs, callback) => {
      checkValidArgs(keyValuePairs, callback);
      return new Promise((resolve, reject) => {
        keyValuePairs.forEach(_ref2 => {
          let [key, value] = _ref2;
          checkValidInput(key, value);
        });
        RCTAsyncStorage.multiSet(keyValuePairs, errors => {
          const error = convertErrors(errors);
          callback === null || callback === void 0 ? void 0 : callback(error);

          if (error) {
            reject(error);
          } else {
            resolve();
          }
        });
      });
    },

    /**
     * Call this to batch the deletion of all keys in the `keys` array.
     *
     * See https://react-native-async-storage.github.io/async-storage/docs/api#multiremove
     */
    multiRemove: (keys, callback) => {
      return new Promise((resolve, reject) => {
        keys.forEach(key => checkValidInput(key));
        RCTAsyncStorage.multiRemove(keys, errors => {
          const error = convertErrors(errors);
          callback === null || callback === void 0 ? void 0 : callback(error);

          if (error) {
            reject(error);
          } else {
            resolve();
          }
        });
      });
    },

    /**
     * Batch operation to merge in existing and new values for a given set of
     * keys. This assumes that the values are stringified JSON.
     *
     * **NOTE**: This is not supported by all native implementations.
     *
     * See https://react-native-async-storage.github.io/async-storage/docs/api#multimerge
     */
    multiMerge: (keyValuePairs, callback) => {
      return new Promise((resolve, reject) => {
        RCTAsyncStorage.multiMerge(keyValuePairs, errors => {
          const error = convertErrors(errors);
          callback === null || callback === void 0 ? void 0 : callback(error);

          if (error) {
            reject(error);
          } else {
            resolve();
          }
        });
      });
    }
  };
})(); // Not all native implementations support merge.


if (!RCTAsyncStorage.multiMerge) {
  delete AsyncStorage.mergeItem;
  delete AsyncStorage.multiMerge;
}

export default AsyncStorage;
//# sourceMappingURL=AsyncStorage.native.js.map