UNPKG

apollo-link-performance

Version:

Easily log and report on the performance of your GraphQL queries.

352 lines (289 loc) 11.1 kB
/*! apollo-link-performance - Easily log and report on the performance of your GraphQL queries. (v1.0.0) !*/ import { ApolloLink } from '@apollo/client'; import { map } from 'rxjs'; function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function toPropertyKey(t) { var i = toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } function _defineProperty(e, r, t) { return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: true, configurable: true, writable: true }) : e[r] = t, e; } const BYTE_UNITS = [ 'B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB', ]; const BIBYTE_UNITS = [ 'B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB', ]; const BIT_UNITS = [ 'b', 'kbit', 'Mbit', 'Gbit', 'Tbit', 'Pbit', 'Ebit', 'Zbit', 'Ybit', ]; const BIBIT_UNITS = [ 'b', 'kibit', 'Mibit', 'Gibit', 'Tibit', 'Pibit', 'Eibit', 'Zibit', 'Yibit', ]; /* Formats the given number using `Number#toLocaleString`. - If locale is a string, the value is expected to be a locale-key (for example: `de`). - If locale is true, the system default locale is used for translation. - If no value for locale is specified, the number is returned unmodified. */ const toLocaleString = (number, locale, options) => { let result = number; if (typeof locale === 'string' || Array.isArray(locale)) { result = number.toLocaleString(locale, options); } else if (locale === true || options !== undefined) { result = number.toLocaleString(undefined, options); } return result; }; const log10 = numberOrBigInt => { if (typeof numberOrBigInt === 'number') { return Math.log10(numberOrBigInt); } const string = numberOrBigInt.toString(10); return string.length + Math.log10(`0.${string.slice(0, 15)}`); }; const log = numberOrBigInt => { if (typeof numberOrBigInt === 'number') { return Math.log(numberOrBigInt); } return log10(numberOrBigInt) * Math.log(10); }; const divide = (numberOrBigInt, divisor) => { if (typeof numberOrBigInt === 'number') { return numberOrBigInt / divisor; } const integerPart = numberOrBigInt / BigInt(divisor); const remainder = numberOrBigInt % BigInt(divisor); return Number(integerPart) + (Number(remainder) / divisor); }; const applyFixedWidth = (result, fixedWidth) => { if (fixedWidth === undefined) { return result; } if (typeof fixedWidth !== 'number' || !Number.isSafeInteger(fixedWidth) || fixedWidth < 0) { throw new TypeError(`Expected fixedWidth to be a non-negative integer, got ${typeof fixedWidth}: ${fixedWidth}`); } if (fixedWidth === 0) { return result; } return result.length < fixedWidth ? result.padStart(fixedWidth, ' ') : result; }; const buildLocaleOptions = options => { const {minimumFractionDigits, maximumFractionDigits} = options; if (minimumFractionDigits === undefined && maximumFractionDigits === undefined) { return undefined; } return { ...(minimumFractionDigits !== undefined && {minimumFractionDigits}), ...(maximumFractionDigits !== undefined && {maximumFractionDigits}), roundingMode: 'trunc', }; }; function prettyBytes(number, options) { if (typeof number !== 'bigint' && !Number.isFinite(number)) { throw new TypeError(`Expected a finite number, got ${typeof number}: ${number}`); } options = { bits: false, binary: false, space: true, nonBreakingSpace: false, ...options, }; const UNITS = options.bits ? (options.binary ? BIBIT_UNITS : BIT_UNITS) : (options.binary ? BIBYTE_UNITS : BYTE_UNITS); const separator = options.space ? (options.nonBreakingSpace ? '\u00A0' : ' ') : ''; // Handle signed zero case const isZero = typeof number === 'number' ? number === 0 : number === 0n; if (options.signed && isZero) { const result = ` 0${separator}${UNITS[0]}`; return applyFixedWidth(result, options.fixedWidth); } const isNegative = number < 0; const prefix = isNegative ? '-' : (options.signed ? '+' : ''); if (isNegative) { number = -number; } const localeOptions = buildLocaleOptions(options); let result; if (number < 1) { const numberString = toLocaleString(number, options.locale, localeOptions); result = prefix + numberString + separator + UNITS[0]; } else { const exponent = Math.min(Math.floor(options.binary ? log(number) / Math.log(1024) : log10(number) / 3), UNITS.length - 1); number = divide(number, (options.binary ? 1024 : 1000) ** exponent); if (!localeOptions) { const minPrecision = Math.max(3, Math.floor(number).toString().length); number = number.toPrecision(minPrecision); } const numberString = toLocaleString(Number(number), options.locale, localeOptions); const unit = UNITS[exponent]; result = prefix + numberString + separator + unit; } return applyFixedWidth(result, options.fixedWidth); } function ownKeys$1(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread$1(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$1(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$1(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } var Duration = { SECOND: 1000, MINUTE: 60000, HOUR: 360000, DAY: 86400000, MONTH: 2592000000, YEAR: 30758400000 }; var DEFAULT_OPTIONS$1 = { minimumIntegerDigits: 1, minimumFractionDigits: 0, maximumFractionDigits: 2 }; /** * quiktime * Quick duration formatting in a condensed format * * @param {number} duration * @param {{ minimumIntegerDigits?: number=, minimumFractionDigits?: number=, maximumFractionDigits?: number= }} [options=] * @return string */ var quiktime = function quiktime(duration, options) { var opts = _objectSpread$1(_objectSpread$1({}, DEFAULT_OPTIONS$1), options); // Milliseconds if (duration < Duration.SECOND) { return "".concat(duration.toLocaleString(undefined, opts), " ms"); } // Seconds if (duration < Duration.MINUTE) { return "".concat((duration / Duration.SECOND).toLocaleString(undefined, opts), " sec"); } // Minutes if (duration < Duration.HOUR) { return "".concat((duration / Duration.MINUTE).toLocaleString(undefined, opts), " min"); } // Hours if (duration < Duration.DAY) { return "".concat((duration / Duration.HOUR).toLocaleString(undefined, opts), " hr"); } // Days if (duration < Duration.MONTH) { return "".concat((duration / Duration.DAY).toLocaleString(undefined, opts), " days"); } // Months if (duration < Duration.YEAR) { return "".concat((duration / Duration.MONTH).toLocaleString(undefined, opts), " mo"); } // Years return "".concat((duration / Duration.YEAR).toLocaleString(undefined, opts), " yr"); }; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), true).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } var isClient = function isClient() { return typeof window !== 'undefined'; }; var DEFAULT_OPTIONS = { debug: false, targetDuration: 500, verbose: false }; /** * performanceLink * Easily log and report on the performance of your GraphQL queries. * * @param {{ debug?: boolean, targetDuration?: number, verbose?: boolean, onRequestStart?: ({ operation, startTime }: { operation: Operation, startTime: number }) => void, onRequestComplete?: ({ data, dataSize, duration, operation }: { data: any, dataSize: number, duration: number, operation: Operation }) => void }} [options=] * @returns ApolloLink */ var performanceLink = function performanceLink() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var _DEFAULT_OPTIONS$opti = _objectSpread(_objectSpread({}, DEFAULT_OPTIONS), options), debug = _DEFAULT_OPTIONS$opti.debug; _DEFAULT_OPTIONS$opti.targetDuration; var verbose = _DEFAULT_OPTIONS$opti.verbose, onRequestStart = _DEFAULT_OPTIONS$opti.onRequestStart, onRequestComplete = _DEFAULT_OPTIONS$opti.onRequestComplete; return new ApolloLink(function (operation, forward) { var startTime = Date.now(); onRequestStart && onRequestStart({ operation: operation, startTime: startTime }); return forward(operation).pipe(map(function (data) { var duration = Date.now() - startTime; var dataSize = new TextEncoder().encode(JSON.stringify(data)).length; if (isClient() && debug) { try { var operationType = operation.query.definitions[0].operation; var groupLabel = "[PerformanceLink] : ".concat(operationType, " : ").concat(operation.operationName, " - ").concat(quiktime(duration)); verbose ? console.group(groupLabel) : console.groupCollapsed(groupLabel); // Duration console.log(' Duration: ' + quiktime(duration)); // Data Size console.log(" Size: ".concat(prettyBytes(dataSize))); // Data console.log(' Data: ', data); // Operation console.log(' Operation: ', operation); console.groupEnd(); } catch (err) { console.error(err); } } onRequestComplete && onRequestComplete({ data: data, dataSize: dataSize, duration: duration, operation: operation }); return data; })); }); }; export { performanceLink }; /* Copyright 2022-2025 — Pregraph (https://www.pregraph.com) | Follow us @pregraph_ */