UNPKG

apollo-link-timeout

Version:

Abort requests that take longer than a specified timeout period

123 lines 6.14 kB
var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); 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); }; // note, this import is modified when building for ESM via `script/fix_apollo_import.mjs` import { ApolloLink, Observable } from '@apollo/client/core/index.js'; import TimeoutError from './TimeoutError.js'; var DEFAULT_TIMEOUT = 15000; /** * Aborts the request if the timeout expires before the response is received. */ var TimeoutLink = /** @class */ (function (_super) { __extends(TimeoutLink, _super); /** * Creates a new TimeoutLink instance. * Aborts the request if the timeout expires before the response is received. * * @param timeout - The timeout in milliseconds for the request. Default is 15000ms (15 seconds). * @param statusCode - The HTTP status code to return when a timeout occurs. Default is 408 (Request Timeout). */ function TimeoutLink(timeout, statusCode) { var _this = _super.call(this) || this; _this.timeout = timeout || DEFAULT_TIMEOUT; _this.statusCode = statusCode; return _this; } TimeoutLink.prototype.request = function (operation, forward) { var _this = this; var controller; var ourController; // override timeout from query context var requestTimeout = operation.getContext().timeout || this.timeout; // add abort controller and signal object to fetchOptions if they don't already exist if (typeof AbortController !== 'undefined') { var context = operation.getContext(); var fetchOptions = context.fetchOptions || {}; ourController = new AbortController(); controller = fetchOptions.controller || ourController; fetchOptions = __assign(__assign({}, fetchOptions), { controller: controller, signal: controller.signal }); operation.setContext({ fetchOptions: fetchOptions }); } var chainObservable = forward(operation); // observable for remaining link chain var operationType = operation.query.definitions.find(function (def) { return def.kind === 'OperationDefinition'; }).operation; if (requestTimeout <= 0 || operationType === 'subscription') { return chainObservable; // skip this link if timeout is zero or it's a subscription request } // create local observable with timeout functionality (unsubscibe from chain observable and // return an error if the timeout expires before chain observable resolves) var localObservable = new Observable(function (observer) { var timer; // listen to chainObservable for result and pass to localObservable if received before timeout var subscription = chainObservable.subscribe(function (result) { clearTimeout(timer); observer.next(result); observer.complete(); }, function (error) { clearTimeout(timer); observer.error(error); observer.complete(); }); // if timeout expires before observable completes, abort call, unsubscribe, and return error timer = setTimeout(function () { if (controller) { if (controller.signal.aborted) { // already aborted from somewhere else return; } controller.abort(); // abort fetch operation // if the AbortController in the operation context is one we created, // it's now "used up", so we need to remove it to avoid blocking any // future retry of the operation. var context = operation.getContext(); var fetchOptions = context.fetchOptions || {}; if (fetchOptions.controller === ourController && fetchOptions.signal === ourController.signal) { operation.setContext(__assign(__assign({}, fetchOptions), { controller: undefined, signal: undefined })); } } observer.error(new TimeoutError('Timeout exceeded', requestTimeout, _this.statusCode)); subscription.unsubscribe(); }, requestTimeout); var cancelTimeout = function () { clearTimeout(timer); subscription.unsubscribe(); }; var ctxRef = operation.getContext().timeoutRef; if (ctxRef) { ctxRef({ unsubscribe: cancelTimeout }); } // cancel timeout if aborted from somewhere else controller.signal.addEventListener("abort", function () { cancelTimeout(); }, { once: true }); // this function is called when a client unsubscribes from localObservable return cancelTimeout; }); return localObservable; }; return TimeoutLink; }(ApolloLink)); export default TimeoutLink; //# sourceMappingURL=timeoutLink.js.map