apollo-link-performance
Version:
Easily log and report on the performance of your GraphQL queries.
352 lines (289 loc) • 11.1 kB
JavaScript
/*! 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_ */