status-checker
Version:
A lean, dependency-free URL status checker library
253 lines • 9.92 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.StatusChecker = void 0;
const http_1 = __importDefault(require("http"));
const https_1 = __importDefault(require("https"));
const url_1 = require("url");
const logger_1 = require("./logger");
const evaluator_1 = require("./evaluator");
/**
* URL Status Checker
*/
class StatusChecker {
/**
* Create a new StatusChecker
* @param config Configuration
* @param options Checker options
*/
constructor(config, options) {
this.config = config;
this.options = {
async: options?.async !== undefined ? options.async : true,
log: options?.log !== undefined ? options.log : true,
logger: options?.logger
};
this.logger = new logger_1.Logger(config);
if (this.options.logger) {
this.logger.log = this.options.logger;
}
}
/**
* Check all URLs in the configuration
* @returns Array of check results
*/
async checkAll() {
this.logger.info(`Starting to check ${this.config.urls.length} URLs`);
if (this.options.async) {
return this.checkAllAsync();
}
else {
return this.checkAllSync();
}
}
/**
* Check all URLs asynchronously
* @returns Array of check results
*/
async checkAllAsync() {
this.logger.debug('Running checks in async mode');
const promises = this.config.urls.map(urlConfig => this.checkUrl(urlConfig));
return Promise.all(promises);
}
/**
* Check all URLs synchronously
* @returns Array of check results
*/
async checkAllSync() {
this.logger.debug('Running checks in sync mode');
const results = [];
for (const urlConfig of this.config.urls) {
const result = await this.checkUrl(urlConfig);
results.push(result);
}
return results;
}
/**
* Check a single URL
* @param urlConfig URL configuration
* @returns Check result
*/
async checkUrl(urlConfig) {
const startTime = Date.now();
const url = urlConfig.url;
const name = urlConfig.name;
const timeout = urlConfig.timeout || this.config.globalTimeout || 5000;
this.logger.debug(`Checking URL: ${url} (${name || ''})`);
try {
// Make the request
const response = await this.makeRequest(urlConfig, timeout);
const responseTime = Date.now() - startTime;
// Create the base result
const checkResult = {
url,
name,
statusCode: response.statusCode,
headers: response.headers,
// Only include body if it's not too large
body: response.body.length > 1024 ? `${response.body.substring(0, 1024)}...` : response.body,
responseTime,
timestamp: new Date(),
success: true, // Will be updated based on checks
checkResults: []
};
// Determine which checks to run
let checks = [];
// If checks are provided, use them
if (urlConfig.checks && urlConfig.checks.length > 0) {
checks = urlConfig.checks;
}
// Otherwise, create a default status code check from successCodes
else {
const successCodes = urlConfig.successCodes || this.config.globalSuccessCodes || [200];
const statusCodeCheck = {
type: 'status_code',
operator: 'equals',
value: successCodes.join('|')
};
checks = [statusCodeCheck];
}
// Run the checks
const evaluator = new evaluator_1.CheckEvaluator();
const checkResults = evaluator.evaluateChecks(checks, {
statusCode: response.statusCode,
headers: response.headers,
body: response.body,
responseTime
});
// Update the result with check results
checkResult.checkResults = checkResults;
// Overall success is true only if all checks passed
checkResult.success = checkResults.every(result => result.passed);
// If any checks failed, add an error message
if (!checkResult.success) {
const failedChecks = checkResults.filter(result => !result.passed);
checkResult.error = failedChecks.map(check => `${check.description} - Expected: ${check.expectedValue}, Actual: ${check.actualValue}`).join('; ');
}
this.logResult(checkResult);
return checkResult;
}
catch (error) {
const responseTime = Date.now() - startTime;
const errorMessage = error instanceof Error ? error.message : String(error);
const checkResult = {
url,
name,
success: false,
error: errorMessage,
responseTime,
timestamp: new Date()
};
this.logResult(checkResult);
return checkResult;
}
}
/**
* Make an HTTP/HTTPS request
* @param urlConfig URL configuration
* @param timeout Timeout in milliseconds
* @returns Promise resolving to the response
*/
makeRequest(urlConfig, timeout) {
return new Promise((resolve, reject) => {
const url = urlConfig.url;
const parsedUrl = new url_1.URL(url);
const isHttps = parsedUrl.protocol === 'https:';
const method = urlConfig.method || 'GET';
const headers = urlConfig.headers || {};
// Add content-type header if body is provided
if (urlConfig.body && urlConfig.contentType && !headers['content-type']) {
headers['content-type'] = urlConfig.contentType;
}
const options = {
hostname: parsedUrl.hostname,
port: parsedUrl.port || (isHttps ? 443 : 80),
path: parsedUrl.pathname + parsedUrl.search,
method,
timeout,
headers
};
const requestModule = isHttps ? https_1.default : http_1.default;
const req = requestModule.request(options, (res) => {
let responseBody = '';
const responseHeaders = {};
// Convert headers to a simple object
for (const [key, value] of Object.entries(res.headers)) {
if (value !== undefined) {
responseHeaders[key] = Array.isArray(value) ? value.join(', ') : value;
}
}
res.setEncoding('utf8');
res.on('data', (chunk) => {
responseBody += chunk;
});
res.on('end', () => {
resolve({
statusCode: res.statusCode || 0,
headers: responseHeaders,
body: responseBody
});
});
});
req.on('error', (error) => {
reject(error);
});
req.on('timeout', () => {
req.destroy();
reject(new Error(`Request timed out after ${timeout}ms`));
});
// Send body if provided and method is not GET or HEAD
if (urlConfig.body && !['GET', 'HEAD'].includes(method)) {
req.write(urlConfig.body);
}
req.end();
});
}
/**
* Log a check result
* @param result Check result
*/
logResult(result) {
if (!this.options.log)
return;
const name = result.name ? `${result.name} (${result.url})` : result.url;
if (result.success) {
this.logger.info(`✅ ${name} - Status: ${result.statusCode} - Response time: ${result.responseTime}ms`);
// Log individual check results in debug mode
if (result.checkResults && result.checkResults.length > 0) {
this.logger.debug(`Check details for ${name}:`);
result.checkResults.forEach(check => {
this.logger.debug(` ✅ ${check.description}`);
});
}
}
else {
this.logger.error(`❌ ${name} - Status: ${result.statusCode} - Response time: ${result.responseTime}ms`);
if (result.error) {
this.logger.error(` Error: ${result.error}`);
}
// Log individual check results
if (result.checkResults && result.checkResults.length > 0) {
this.logger.debug(`Check details for ${name}:`);
result.checkResults.forEach(check => {
const symbol = check.passed ? '✅' : '❌';
const message = ` ${symbol} ${check.description}`;
if (check.passed) {
this.logger.debug(message);
}
else {
this.logger.error(message);
if (check.error) {
this.logger.error(` Error: ${check.error}`);
}
this.logger.error(` Expected: ${check.expectedValue}, Actual: ${check.actualValue}`);
}
});
}
}
}
}
exports.StatusChecker = StatusChecker;
//# sourceMappingURL=checker.js.map