@checkfirst/nestjs-outlook
Version:
An opinionated NestJS module for Microsoft Outlook integration that provides easy access to Microsoft Graph API for emails, calendars, and more.
184 lines • 7.06 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.delay = delay;
exports.isNonRetryableError = isNonRetryableError;
exports.is410Error = is410Error;
exports.is429Error = is429Error;
exports.is404Error = is404Error;
exports.isNetworkError = isNetworkError;
exports.isServerError = isServerError;
exports.extractRetryAfterSeconds = extractRetryAfterSeconds;
exports.retryWithBackoff = retryWithBackoff;
async function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function isGraphErrorWithStatus(error, statusCode) {
if (!error || typeof error !== 'object') {
return false;
}
if ('statusCode' in error && typeof error.statusCode === 'number') {
return error.statusCode === statusCode;
}
if ('stack' in error && Array.isArray(error.stack) && error.stack.length > 0) {
const firstError = error.stack[0];
if (firstError && typeof firstError === 'object' && 'statusCode' in firstError) {
return firstError.statusCode === statusCode;
}
}
return false;
}
function isNonRetryableError(error) {
return (isGraphErrorWithStatus(error, 401) ||
isGraphErrorWithStatus(error, 403) ||
isGraphErrorWithStatus(error, 404) ||
isGraphErrorWithStatus(error, 410));
}
function is410Error(error) {
return isGraphErrorWithStatus(error, 410);
}
function is429Error(error) {
return isGraphErrorWithStatus(error, 429);
}
function is404Error(error) {
return isGraphErrorWithStatus(error, 404);
}
function isNetworkError(error) {
if (!error || typeof error !== 'object') {
return false;
}
if ('code' in error) {
const code = String(error.code);
return code === 'ECONNABORTED' || code === 'ETIMEDOUT' || code === 'ENOTFOUND' || code === 'ECONNRESET';
}
return false;
}
function isServerError(error) {
if (!error || typeof error !== 'object') {
return false;
}
if ('statusCode' in error && typeof error.statusCode === 'number') {
return error.statusCode >= 500 && error.statusCode < 600;
}
if ('stack' in error && Array.isArray(error.stack) && error.stack.length > 0) {
const firstError = error.stack[0];
if (firstError && typeof firstError === 'object' && 'statusCode' in firstError) {
const statusCode = firstError.statusCode;
return statusCode >= 500 && statusCode < 600;
}
}
return false;
}
function extractRetryAfterSeconds(error) {
if (!error || typeof error !== 'object') {
return null;
}
if ('response' in error && error.response && typeof error.response === 'object') {
const response = error.response;
if (response.headers && 'retry-after' in response.headers) {
const retryAfter = response.headers['retry-after'];
if (typeof retryAfter === 'string') {
const parsed = parseInt(retryAfter, 10);
if (!isNaN(parsed)) {
return Math.max(parsed, 5);
}
}
else if (typeof retryAfter === 'number') {
return Math.max(retryAfter, 5);
}
}
}
return null;
}
async function retryWithBackoff(operation, options) {
var _a, _b, _c, _d;
const maxRetries = (_a = options === null || options === void 0 ? void 0 : options.maxRetries) !== null && _a !== void 0 ? _a : 3;
const retryDelayMs = (_b = options === null || options === void 0 ? void 0 : options.retryDelayMs) !== null && _b !== void 0 ? _b : 1000;
const retryCount = (_c = options === null || options === void 0 ? void 0 : options.retryCount) !== null && _c !== void 0 ? _c : 0;
const logger = options === null || options === void 0 ? void 0 : options.logger;
const operationName = (_d = options === null || options === void 0 ? void 0 : options.operationName) !== null && _d !== void 0 ? _d : 'operation';
try {
return await operation();
}
catch (error) {
const errorDetails = extractErrorInfo(error);
if (isNonRetryableError(error)) {
if (logger) {
logger.warn(`[retryWithBackoff] Non-retryable error for ${operationName}, not retrying`, {
statusCode: errorDetails.statusCode,
errorCode: errorDetails.code,
});
}
throw error;
}
if (retryCount >= maxRetries) {
if (logger) {
logger.warn(`[retryWithBackoff] Max retries (${maxRetries}) exceeded for ${operationName}`, {
statusCode: errorDetails.statusCode,
errorCode: errorDetails.code,
errorMessage: errorDetails.message,
});
}
throw error;
}
const delayMs = retryDelayMs * Math.pow(2, retryCount);
if (logger) {
logger.warn(`[retryWithBackoff] Retry ${retryCount + 1}/${maxRetries} for ${operationName} after ${delayMs}ms`, {
statusCode: errorDetails.statusCode,
errorCode: errorDetails.code,
errorType: errorDetails.type,
delayMs,
});
}
await delay(delayMs);
return retryWithBackoff(operation, {
maxRetries,
retryDelayMs,
retryCount: retryCount + 1,
logger,
operationName,
});
}
}
function extractErrorInfo(error) {
if (!error || typeof error !== 'object') {
return {
statusCode: 'N/A',
code: 'N/A',
message: String(error),
type: 'unknown',
};
}
if ('statusCode' in error) {
const statusCode = error.statusCode;
const code = 'code' in error ? String(error.code) : 'N/A';
const body = 'body' in error ? String(error.body) : 'N/A';
return {
statusCode,
code,
message: body,
type: statusCode === -1 ? 'network_error' : 'graph_api_error',
};
}
if ('stack' in error && Array.isArray(error.stack) && error.stack.length > 0) {
const firstError = error.stack[0];
if (firstError && typeof firstError === 'object') {
const statusCode = 'statusCode' in firstError ? firstError.statusCode : 'N/A';
const code = 'code' in firstError ? String(firstError.code) : 'N/A';
const body = 'body' in firstError ? String(firstError.body) : 'N/A';
return {
statusCode,
code,
message: body,
type: typeof statusCode === 'number' && statusCode === -1 ? 'network_error' : 'graph_api_error',
};
}
}
const errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
return {
statusCode: 'N/A',
code: 'N/A',
message: errorMessage,
type: 'generic_error',
};
}
//# sourceMappingURL=retry.util.js.map