"use strict";
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
var __read = (this && this.__read) || function (o, n) {
    var m = typeof Symbol === "function" && o[Symbol.iterator];
    if (!m) return o;
    var i = m.call(o), r, ar = [], e;
    try {
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
    }
    catch (error) { e = { error: error }; }
    finally {
        try {
            if (r && !r.done && (m = i["return"])) m.call(i);
        }
        finally { if (e) throw e.error; }
    }
    return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
    if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
            if (!ar) ar = Array.prototype.slice.call(from, 0, i);
            ar[i] = from[i];
        }
    }
    return to.concat(ar || Array.prototype.slice.call(from));
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.mutate = void 0;
/**              ____
 *--------------/    \.------------------/
 *            /  swrv  \.               /    //
 *          /         / /\.            /    //
 *        /     _____/ /   \.         /
 *      /      /  ____/   .  \.      /
 *    /        \ \_____        \.   /
 *  /     .     \_____ \         \ /    //
 *  \          _____/ /        ./ /    //
 *    \       / _____/       ./  /
 *      \    / /      .    ./   /
 *        \ / /          ./    /
 *    .     \/         ./     /    //
 *            \      ./      /    //
 *              \.. /       /
 *         .     |||       /
 *               |||      /
 *     .         |||     /    //
 *               |||    /    //
 *               |||   /
 */
var vue_1 = require("vue");
var web_preset_1 = __importDefault(require("./lib/web-preset"));
var cache_1 = __importDefault(require("./cache"));
var DATA_CACHE = new cache_1.default();
var REF_CACHE = new cache_1.default();
var PROMISES_CACHE = new cache_1.default();
var defaultConfig = {
    cache: DATA_CACHE,
    refreshInterval: 0,
    ttl: 0,
    serverTTL: 1000,
    dedupingInterval: 2000,
    revalidateOnFocus: true,
    revalidateDebounce: 0,
    shouldRetryOnError: true,
    errorRetryInterval: 5000,
    errorRetryCount: 5,
    fetcher: web_preset_1.default.fetcher,
    isOnline: web_preset_1.default.isOnline,
    isDocumentVisible: web_preset_1.default.isDocumentVisible
};
/**
 * Cache the refs for later revalidation
 */
function setRefCache(key, theRef, ttl) {
    var refCacheItem = REF_CACHE.get(key);
    if (refCacheItem) {
        refCacheItem.data.push(theRef);
    }
    else {
        // #51 ensures ref cache does not evict too soon
        var gracePeriod = 5000;
        REF_CACHE.set(key, [theRef], ttl > 0 ? ttl + gracePeriod : ttl);
    }
}
function onErrorRetry(revalidate, errorRetryCount, config) {
    if (!config.isDocumentVisible()) {
        return;
    }
    if (config.errorRetryCount !== undefined && errorRetryCount > config.errorRetryCount) {
        return;
    }
    var count = Math.min(errorRetryCount || 0, config.errorRetryCount);
    var timeout = count * config.errorRetryInterval;
    setTimeout(function () {
        revalidate(null, { errorRetryCount: count + 1, shouldRetryOnError: true });
    }, timeout);
}
/**
 * Main mutation function for receiving data from promises to change state and
 * set data cache
 */
var mutate = function (key, res, cache, ttl) {
    if (cache === void 0) { cache = DATA_CACHE; }
    if (ttl === void 0) { ttl = defaultConfig.ttl; }
    return __awaiter(void 0, void 0, void 0, function () {
        var data, error, isValidating, err_1, newData, stateRef, refs_1;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    if (!isPromise(res)) return [3 /*break*/, 5];
                    _a.label = 1;
                case 1:
                    _a.trys.push([1, 3, , 4]);
                    return [4 /*yield*/, res];
                case 2:
                    data = _a.sent();
                    return [3 /*break*/, 4];
                case 3:
                    err_1 = _a.sent();
                    error = err_1;
                    return [3 /*break*/, 4];
                case 4: return [3 /*break*/, 6];
                case 5:
                    data = res;
                    _a.label = 6;
                case 6:
                    // eslint-disable-next-line prefer-const
                    isValidating = false;
                    newData = { data: data, error: error, isValidating: isValidating };
                    if (typeof data !== 'undefined') {
                        try {
                            cache.set(key, newData, ttl);
                        }
                        catch (err) {
                            console.error('swrv(mutate): failed to set cache', err);
                        }
                    }
                    stateRef = REF_CACHE.get(key);
                    if (stateRef && stateRef.data.length) {
                        refs_1 = stateRef.data.filter(function (r) { return r.key === key; });
                        refs_1.forEach(function (r, idx) {
                            if (typeof newData.data !== 'undefined') {
                                r.data = newData.data;
                            }
                            r.error = newData.error;
                            r.isValidating = newData.isValidating;
                            r.isLoading = newData.isValidating;
                            var isLast = idx === refs_1.length - 1;
                            if (!isLast) {
                                // Clean up refs that belonged to old keys
                                delete refs_1[idx];
                            }
                        });
                        refs_1 = refs_1.filter(Boolean);
                    }
                    return [2 /*return*/, newData];
            }
        });
    });
};
exports.mutate = mutate;
function useSWRV() {
    var _this = this;
    var args = [];
    for (var _i = 0; _i < arguments.length; _i++) {
        args[_i] = arguments[_i];
    }
    var key;
    var fn;
    var config = __assign({}, defaultConfig);
    var unmounted = false;
    var isHydrated = false;
    var instance = (0, vue_1.getCurrentInstance)();
    var vm = (instance === null || instance === void 0 ? void 0 : instance.proxy) || instance; // https://github.com/vuejs/composition-api/pull/520
    if (!vm) {
        console.error('Could not get current instance, check to make sure that `useSwrv` is declared in the top level of the setup function.');
        return null;
    }
    var IS_SERVER = (vm === null || vm === void 0 ? void 0 : vm.$isServer) || false;
    // #region ssr
    /**
    const isSsrHydration = Boolean(
      \!IS_SERVER &&
      vm.$vnode &&
      vm.$vnode.elm &&
      vm.$vnode.elm.dataset &&
      vm.$vnode.elm.dataset.swrvKey)
    */
    // #endregion
    if (args.length >= 1) {
        key = args[0];
    }
    if (args.length >= 2) {
        fn = args[1];
    }
    if (args.length > 2) {
        config = __assign(__assign({}, config), args[2]);
    }
    var ttl = IS_SERVER ? config.serverTTL : config.ttl;
    var keyRef = typeof key === 'function' ? key : (0, vue_1.ref)(key);
    if (typeof fn === 'undefined') {
        // use the global fetcher
        fn = config.fetcher;
    }
    var stateRef = null;
    // #region ssr
    // if (isSsrHydration) {
    //   // component was ssrHydrated, so make the ssr reactive as the initial data
    //   const swrvState = (window as any).__SWRV_STATE__ ||
    //     ((window as any).__NUXT__ && (window as any).__NUXT__.swrv) || []
    //   const swrvKey = +(vm as any).$vnode.elm.dataset.swrvKey
    //   if (swrvKey !== undefined && swrvKey !== null) {
    //     const nodeState = swrvState[swrvKey] || []
    //     const instanceState = nodeState[isRef(keyRef) ? keyRef.value : keyRef()]
    //     if (instanceState) {
    //       stateRef = reactive(instanceState)
    //       isHydrated = true
    //     }
    //   }
    // }
    // #endregion
    if (!stateRef) {
        stateRef = (0, vue_1.reactive)({
            data: undefined,
            error: undefined,
            isValidating: true,
            isLoading: true,
            key: null
        });
    }
    /**
     * Revalidate the cache, mutate data
     */
    var revalidate = function (data, opts) { return __awaiter(_this, void 0, void 0, function () {
        var isFirstFetch, keyVal, cacheItem, newData, fetcher, shouldRevalidate, trigger;
        var _this = this;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    isFirstFetch = stateRef.data === undefined;
                    keyVal = keyRef.value;
                    if (!keyVal) {
                        return [2 /*return*/];
                    }
                    cacheItem = config.cache.get(keyVal);
                    newData = cacheItem && cacheItem.data;
                    stateRef.isValidating = true;
                    stateRef.isLoading = !newData;
                    if (newData) {
                        stateRef.data = newData.data;
                        stateRef.error = newData.error;
                    }
                    fetcher = data || fn;
                    if (!fetcher ||
                        (!config.isDocumentVisible() && !isFirstFetch) ||
                        ((opts === null || opts === void 0 ? void 0 : opts.forceRevalidate) !== undefined && !(opts === null || opts === void 0 ? void 0 : opts.forceRevalidate))) {
                        stateRef.isValidating = false;
                        stateRef.isLoading = false;
                        return [2 /*return*/];
                    }
                    // Dedupe items that were created in the last interval #76
                    if (cacheItem) {
                        shouldRevalidate = Boolean(((Date.now() - cacheItem.createdAt) >= config.dedupingInterval) || (opts === null || opts === void 0 ? void 0 : opts.forceRevalidate));
                        if (!shouldRevalidate) {
                            stateRef.isValidating = false;
                            stateRef.isLoading = false;
                            return [2 /*return*/];
                        }
                    }
                    trigger = function () { return __awaiter(_this, void 0, void 0, function () {
                        var promiseFromCache, fetcherArgs, newPromise, shouldRetryOnError;
                        return __generator(this, function (_a) {
                            switch (_a.label) {
                                case 0:
                                    promiseFromCache = PROMISES_CACHE.get(keyVal);
                                    if (!!promiseFromCache) return [3 /*break*/, 2];
                                    fetcherArgs = Array.isArray(keyVal) ? keyVal : [keyVal];
                                    newPromise = fetcher.apply(void 0, __spreadArray([], __read(fetcherArgs), false));
                                    PROMISES_CACHE.set(keyVal, newPromise, config.dedupingInterval);
                                    return [4 /*yield*/, mutate(keyVal, newPromise, config.cache, ttl)];
                                case 1:
                                    _a.sent();
                                    return [3 /*break*/, 4];
                                case 2: return [4 /*yield*/, mutate(keyVal, promiseFromCache.data, config.cache, ttl)];
                                case 3:
                                    _a.sent();
                                    _a.label = 4;
                                case 4:
                                    stateRef.isValidating = false;
                                    stateRef.isLoading = false;
                                    PROMISES_CACHE.delete(keyVal);
                                    if (stateRef.error !== undefined) {
                                        shouldRetryOnError = !unmounted && config.shouldRetryOnError && (opts ? opts.shouldRetryOnError : true);
                                        if (shouldRetryOnError) {
                                            onErrorRetry(revalidate, opts ? opts.errorRetryCount : 1, config);
                                        }
                                    }
                                    return [2 /*return*/];
                            }
                        });
                    }); };
                    if (!(newData && config.revalidateDebounce)) return [3 /*break*/, 1];
                    setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
                        return __generator(this, function (_a) {
                            switch (_a.label) {
                                case 0:
                                    if (!!unmounted) return [3 /*break*/, 2];
                                    return [4 /*yield*/, trigger()];
                                case 1:
                                    _a.sent();
                                    _a.label = 2;
                                case 2: return [2 /*return*/];
                            }
                        });
                    }); }, config.revalidateDebounce);
                    return [3 /*break*/, 3];
                case 1: return [4 /*yield*/, trigger()];
                case 2:
                    _a.sent();
                    _a.label = 3;
                case 3: return [2 /*return*/];
            }
        });
    }); };
    var revalidateCall = function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
        return [2 /*return*/, revalidate(null, { shouldRetryOnError: false })];
    }); }); };
    var timer = null;
    /**
     * Setup polling
     */
    (0, vue_1.onMounted)(function () {
        var tick = function () { return __awaiter(_this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (!(!stateRef.error && config.isOnline())) return [3 /*break*/, 2];
                        // if API request errored, we stop polling in this round
                        // and let the error retry function handle it
                        return [4 /*yield*/, revalidate()];
                    case 1:
                        // if API request errored, we stop polling in this round
                        // and let the error retry function handle it
                        _a.sent();
                        return [3 /*break*/, 3];
                    case 2:
                        if (timer) {
                            clearTimeout(timer);
                        }
                        _a.label = 3;
                    case 3:
                        if (config.refreshInterval && !unmounted) {
                            timer = setTimeout(tick, config.refreshInterval);
                        }
                        return [2 /*return*/];
                }
            });
        }); };
        if (config.refreshInterval) {
            timer = setTimeout(tick, config.refreshInterval);
        }
        if (config.revalidateOnFocus) {
            document.addEventListener('visibilitychange', revalidateCall, false);
            window.addEventListener('focus', revalidateCall, false);
        }
    });
    /**
     * Teardown
     */
    (0, vue_1.onUnmounted)(function () {
        unmounted = true;
        if (timer) {
            clearTimeout(timer);
        }
        if (config.revalidateOnFocus) {
            document.removeEventListener('visibilitychange', revalidateCall, false);
            window.removeEventListener('focus', revalidateCall, false);
        }
        var refCacheItem = REF_CACHE.get(keyRef.value);
        if (refCacheItem) {
            refCacheItem.data = refCacheItem.data.filter(function (ref) { return ref !== stateRef; });
        }
    });
    // #region ssr
    // if (IS_SERVER) {
    //   // make sure srwv exists in ssrContext
    //   let swrvRes = []
    //   if (vm.$ssrContext) {
    //     swrvRes = vm.$ssrContext.swrv = vm.$ssrContext.swrv || swrvRes
    //   }
    //   const ssrKey = swrvRes.length
    //   if (!vm.$vnode || (vm.$node && !vm.$node.data)) {
    //     vm.$vnode = {
    //       data: { attrs: { 'data-swrv-key': ssrKey } }
    //     }
    //   }
    //   const attrs = (vm.$vnode.data.attrs = vm.$vnode.data.attrs || {})
    //   attrs['data-swrv-key'] = ssrKey
    //   // Nuxt compatibility
    //   if (vm.$ssrContext && vm.$ssrContext.nuxt) {
    //     vm.$ssrContext.nuxt.swrv = swrvRes
    //   }
    //   onServerPrefetch(async () => {
    //     await revalidate()
    //     if (!swrvRes[ssrKey]) swrvRes[ssrKey] = {}
    //     swrvRes[ssrKey][keyRef.value] = {
    //       data: stateRef.data,
    //       error: stateRef.error,
    //       isValidating: stateRef.isValidating
    //     }
    //   })
    // }
    // #endregion
    /**
     * Revalidate when key dependencies change
     */
    try {
        (0, vue_1.watch)(keyRef, function (val) {
            if (!(0, vue_1.isReadonly)(keyRef)) {
                keyRef.value = val;
            }
            stateRef.key = val;
            stateRef.isValidating = Boolean(val);
            setRefCache(keyRef.value, stateRef, ttl);
            if (!IS_SERVER && !isHydrated && keyRef.value) {
                revalidate();
            }
            isHydrated = false;
        }, {
            immediate: true
        });
    }
    catch (_a) {
        // do nothing
    }
    var res = __assign(__assign({}, (0, vue_1.toRefs)(stateRef)), { mutate: function (data, opts) { return revalidate(data, __assign(__assign({}, opts), { forceRevalidate: true })); } });
    return res;
}
function isPromise(p) {
    return p !== null && typeof p === 'object' && typeof p.then === 'function';
}
exports.default = useSWRV;
//# sourceMappingURL=use-swrv.js.map