apollo-link-timeout
Version:
Abort requests that take longer than a specified timeout period
123 lines • 6.14 kB
JavaScript
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