import { __assign } from "tslib";
import { invariant } from "../../utilities/globals/index.js";
import { print } from 'graphql';
import { ApolloLink } from "../core/index.js";
import { Observable, compact, isNonEmptyArray, } from "../../utilities/index.js";
export var VERSION = 1;
export var PersistedQueryLink;
(function (PersistedQueryLink) {
    ;
    ;
    ;
})(PersistedQueryLink || (PersistedQueryLink = {}));
function collectErrorsByMessage(graphQLErrors) {
    var collected = Object.create(null);
    if (isNonEmptyArray(graphQLErrors)) {
        graphQLErrors.forEach(function (error) { return collected[error.message] = error; });
    }
    return collected;
}
var defaultOptions = {
    disable: function (_a) {
        var graphQLErrors = _a.graphQLErrors, operation = _a.operation;
        var errorMessages = collectErrorsByMessage(graphQLErrors);
        if (errorMessages.PersistedQueryNotSupported) {
            return true;
        }
        if (errorMessages.PersistedQueryNotFound) {
            return false;
        }
        var response = operation.getContext().response;
        if (response &&
            response.status &&
            (response.status === 400 || response.status === 500)) {
            return true;
        }
        return false;
    },
    useGETForHashedQueries: false,
};
function operationDefinesMutation(operation) {
    return operation.query.definitions.some(function (d) { return d.kind === 'OperationDefinition' && d.operation === 'mutation'; });
}
var hasOwnProperty = Object.prototype.hasOwnProperty;
var hashesByQuery = new WeakMap();
var nextHashesChildKey = 0;
export var createPersistedQueryLink = function (options) {
    __DEV__ ? invariant(options && (typeof options.sha256 === 'function' ||
        typeof options.generateHash === 'function'), 'Missing/invalid "sha256" or "generateHash" function. Please ' +
        'configure one using the "createPersistedQueryLink(options)" options ' +
        'parameter.') : invariant(options && (typeof options.sha256 === 'function' ||
        typeof options.generateHash === 'function'), 24);
    var _a = compact(defaultOptions, options), sha256 = _a.sha256, _b = _a.generateHash, generateHash = _b === void 0 ? function (query) {
        return Promise.resolve(sha256(print(query)));
    } : _b, disable = _a.disable, useGETForHashedQueries = _a.useGETForHashedQueries;
    var supportsPersistedQueries = true;
    var hashesChildKey = 'forLink' + nextHashesChildKey++;
    var getHashPromise = function (query) {
        return new Promise(function (resolve) { return resolve(generateHash(query)); });
    };
    function getQueryHash(query) {
        if (!query || typeof query !== 'object') {
            return getHashPromise(query);
        }
        var hashes = hashesByQuery.get(query);
        if (!hashes)
            hashesByQuery.set(query, hashes = Object.create(null));
        return hasOwnProperty.call(hashes, hashesChildKey)
            ? hashes[hashesChildKey]
            : hashes[hashesChildKey] = getHashPromise(query);
    }
    return new ApolloLink(function (operation, forward) {
        __DEV__ ? invariant(forward, 'PersistedQueryLink cannot be the last link in the chain.') : invariant(forward, 25);
        var query = operation.query;
        return new Observable(function (observer) {
            var subscription;
            var retried = false;
            var originalFetchOptions;
            var setFetchOptions = false;
            var retry = function (_a, cb) {
                var response = _a.response, networkError = _a.networkError;
                if (!retried && ((response && response.errors) || networkError)) {
                    retried = true;
                    var graphQLErrors = [];
                    var responseErrors = response && response.errors;
                    if (isNonEmptyArray(responseErrors)) {
                        graphQLErrors.push.apply(graphQLErrors, responseErrors);
                    }
                    var networkErrors = networkError &&
                        networkError.result &&
                        networkError.result.errors;
                    if (isNonEmptyArray(networkErrors)) {
                        graphQLErrors.push.apply(graphQLErrors, networkErrors);
                    }
                    var disablePayload = {
                        response: response,
                        networkError: networkError,
                        operation: operation,
                        graphQLErrors: isNonEmptyArray(graphQLErrors) ? graphQLErrors : void 0,
                    };
                    supportsPersistedQueries = !disable(disablePayload);
                    if (collectErrorsByMessage(graphQLErrors).PersistedQueryNotFound ||
                        !supportsPersistedQueries) {
                        if (subscription)
                            subscription.unsubscribe();
                        operation.setContext({
                            http: {
                                includeQuery: true,
                                includeExtensions: supportsPersistedQueries,
                            },
                            fetchOptions: {
                                method: 'POST',
                            },
                        });
                        if (setFetchOptions) {
                            operation.setContext({ fetchOptions: originalFetchOptions });
                        }
                        subscription = forward(operation).subscribe(handler);
                        return;
                    }
                }
                cb();
            };
            var handler = {
                next: function (response) {
                    retry({ response: response }, function () { return observer.next(response); });
                },
                error: function (networkError) {
                    retry({ networkError: networkError }, function () { return observer.error(networkError); });
                },
                complete: observer.complete.bind(observer),
            };
            operation.setContext({
                http: {
                    includeQuery: !supportsPersistedQueries,
                    includeExtensions: supportsPersistedQueries,
                },
            });
            if (useGETForHashedQueries &&
                supportsPersistedQueries &&
                !operationDefinesMutation(operation)) {
                operation.setContext(function (_a) {
                    var _b = _a.fetchOptions, fetchOptions = _b === void 0 ? {} : _b;
                    originalFetchOptions = fetchOptions;
                    return {
                        fetchOptions: __assign(__assign({}, fetchOptions), { method: 'GET' }),
                    };
                });
                setFetchOptions = true;
            }
            if (supportsPersistedQueries) {
                getQueryHash(query).then(function (sha256Hash) {
                    operation.extensions.persistedQuery = {
                        version: VERSION,
                        sha256Hash: sha256Hash,
                    };
                    subscription = forward(operation).subscribe(handler);
                }).catch(observer.error.bind(observer));
                ;
            }
            else {
                subscription = forward(operation).subscribe(handler);
            }
            return function () {
                if (subscription)
                    subscription.unsubscribe();
            };
        });
    });
};
//# sourceMappingURL=index.js.map