UNPKG

@graphql-hive/core

Version:
125 lines (124 loc) • 5.91 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.URL = exports.http = void 0; exports.makeFetchCall = makeFetchCall; const tslib_1 = require("tslib"); const async_retry_1 = tslib_1.__importDefault(require("async-retry")); const fetch_1 = require("@whatwg-node/fetch"); Object.defineProperty(exports, "URL", { enumerable: true, get: function () { return fetch_1.URL; } }); function get(endpoint, config) { return makeFetchCall(endpoint, { method: 'GET', headers: config.headers, timeout: config.timeout, retry: config.retry, fetchImplementation: config.fetchImplementation, logger: config.logger, isRequestOk: config.isRequestOk, }); } function post(endpoint, data, config) { return makeFetchCall(endpoint, Object.assign({ body: data, method: 'POST' }, config)); } exports.http = { get, post, }; async function makeFetchCall(endpoint, config) { var _a, _b, _c, _d, _e, _f, _g, _h, _j; const logger = config.logger; const isRequestOk = (_a = config.isRequestOk) !== null && _a !== void 0 ? _a : (response => response.ok); let retries = 0; let minTimeout = 200; let maxTimeout = 2000; let factor = 1.2; if (config.retry !== false) { retries = (_c = (_b = config.retry) === null || _b === void 0 ? void 0 : _b.retries) !== null && _c !== void 0 ? _c : 5; minTimeout = (_e = (_d = config.retry) === null || _d === void 0 ? void 0 : _d.minTimeout) !== null && _e !== void 0 ? _e : 200; maxTimeout = (_g = (_f = config.retry) === null || _f === void 0 ? void 0 : _f.maxTimeout) !== null && _g !== void 0 ? _g : 2000; factor = (_j = (_h = config.retry) === null || _h === void 0 ? void 0 : _h.factor) !== null && _j !== void 0 ? _j : 1.2; } return await (0, async_retry_1.default)(async (bail, attempt) => { var _a, _b; const requestId = fetch_1.crypto.randomUUID(); logger === null || logger === void 0 ? void 0 : logger.info(`${config.method} ${endpoint} (x-request-id=${requestId})` + (retries > 0 ? ' ' + getAttemptMessagePart(attempt, retries + 1) : '')); const getDuration = measureTime(); const signal = AbortSignal.timeout((_a = config.timeout) !== null && _a !== void 0 ? _a : 20000); const response = await ((_b = config.fetchImplementation) !== null && _b !== void 0 ? _b : fetch_1.fetch)(endpoint, { method: config.method, body: config.body, headers: Object.assign({ 'x-request-id': requestId }, config.headers), signal, }).catch((error) => { const logErrorMessage = () => logger === null || logger === void 0 ? void 0 : logger.error(`${config.method} ${endpoint} (x-request-id=${requestId}) failed ${getDuration()}. ` + getErrorMessage(error)); if (isAggregateError(error)) { for (const err of error.errors) { logger === null || logger === void 0 ? void 0 : logger.error(err); } logErrorMessage(); throw new Error(`Unexpected HTTP error. (x-request-id=${requestId})`, { cause: error }); } logger === null || logger === void 0 ? void 0 : logger.error(error); logErrorMessage(); throw new Error(`Unexpected HTTP error. (x-request-id=${requestId})`, { cause: error }); }); if (isRequestOk(response)) { logger === null || logger === void 0 ? void 0 : logger.info(`${config.method} ${endpoint} (x-request-id=${requestId}) succeeded with status ${response.status} ${getDuration()}.`); return response; } logger === null || logger === void 0 ? void 0 : logger.error(`${config.method} ${endpoint} (x-request-id=${requestId}) failed with status ${response.status} ${getDuration()}: ${(await response.text()) || '<empty response body>'}`); if (retries > 0 && attempt > retries) { logger === null || logger === void 0 ? void 0 : logger.error(`${config.method} ${endpoint} (x-request-id=${requestId}) retry limit exceeded after ${attempt} attempts.`); } const error = new Error(`${config.method} ${endpoint} (x-request-id=${requestId}) failed with status ${response.status}.`); if (response.status >= 400 && response.status < 500) { if (retries > 0) { logger === null || logger === void 0 ? void 0 : logger.error(`Abort retry because of status code ${response.status}.`); } bail(error); } throw error; }, { retries, minTimeout, maxTimeout, factor, }); } function getErrorMessage(error) { if (error && typeof error === 'object' && 'message' in error) { return String(error.message); } return '<no error message>'; } function getAttemptMessagePart(attempt, retry) { return `Attempt (${attempt}/${retry})`; } function measureTime() { const start = Date.now(); return () => '(' + formatTimestamp(Date.now() - start) + ')'; } function formatTimestamp(timestamp) { const milliseconds = timestamp % 1000; const seconds = Math.floor((timestamp / 1000) % 60); const minutes = Math.floor((timestamp / (1000 * 60)) % 60); const hours = Math.floor(timestamp / (1000 * 60 * 60)); const parts = []; if (hours > 0) { parts.push(`${hours}h`); } if (minutes > 0 || hours > 0) { // Include minutes if hours exist, even if minutes are 0 parts.push(`${minutes}m`); } if (seconds > 0 || minutes > 0 || hours > 0) { parts.push(`${seconds}s`); } parts.push(`${milliseconds}ms`); return parts.join(':'); } function isAggregateError(error) { return !!error && typeof error === 'object' && 'errors' in error && Array.isArray(error.errors); }