"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RetryLink = void 0;
const rxjs_1 = require("rxjs");
const errors_1 = require("@apollo/client/errors");
const link_1 = require("@apollo/client/link");
const delayFunction_js_1 = require("./delayFunction.cjs");
const retryFunction_js_1 = require("./retryFunction.cjs");
/**
 * Tracking and management of operations that may be (or currently are) retried.
 */
class RetryableOperation {
    observer;
    operation;
    forward;
    delayFor;
    retryIf;
    retryCount = 0;
    currentSubscription = null;
    timerId;
    constructor(observer, operation, forward, delayFor, retryIf) {
        this.observer = observer;
        this.operation = operation;
        this.forward = forward;
        this.delayFor = delayFor;
        this.retryIf = retryIf;
        this.try();
    }
    /**
     * Stop retrying for the operation, and cancel any in-progress requests.
     */
    cancel() {
        if (this.currentSubscription) {
            this.currentSubscription.unsubscribe();
        }
        clearTimeout(this.timerId);
        this.timerId = undefined;
        this.currentSubscription = null;
    }
    try() {
        this.currentSubscription = this.forward(this.operation).subscribe({
            next: (result) => {
                if ((0, errors_1.graphQLResultHasProtocolErrors)(result)) {
                    this.onError(result.extensions[errors_1.PROTOCOL_ERRORS_SYMBOL]);
                    // Unsubscribe from the current subscription to prevent the `complete`
                    // handler to be called as a result of the stream closing.
                    this.currentSubscription?.unsubscribe();
                    return;
                }
                this.observer.next(result);
            },
            error: this.onError,
            complete: this.observer.complete.bind(this.observer),
        });
    }
    onError = async (error) => {
        this.retryCount += 1;
        // Should we retry?
        const shouldRetry = await this.retryIf(this.retryCount, this.operation, error);
        if (shouldRetry) {
            this.scheduleRetry(this.delayFor(this.retryCount, this.operation, error));
            return;
        }
        this.observer.error(error);
    };
    scheduleRetry(delay) {
        if (this.timerId) {
            throw new Error(`RetryLink BUG! Encountered overlapping retries`);
        }
        this.timerId = setTimeout(() => {
            this.timerId = undefined;
            this.try();
        }, delay);
    }
}
class RetryLink extends link_1.ApolloLink {
    delayFor;
    retryIf;
    constructor(options) {
        super();
        const { attempts, delay } = options || {};
        this.delayFor =
            typeof delay === "function" ? delay : (0, delayFunction_js_1.buildDelayFunction)(delay);
        this.retryIf =
            typeof attempts === "function" ? attempts : (0, retryFunction_js_1.buildRetryFunction)(attempts);
    }
    request(operation, nextLink) {
        return new rxjs_1.Observable((observer) => {
            const retryable = new RetryableOperation(observer, operation, nextLink, this.delayFor, this.retryIf);
            return () => {
                retryable.cancel();
            };
        });
    }
}
exports.RetryLink = RetryLink;
//# sourceMappingURL=retryLink.cjs.map
