status-checker
Version:
A lean, dependency-free URL status checker library
366 lines • 13.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CheckEvaluator = void 0;
/**
* Check evaluator for evaluating different types of checks
*/
class CheckEvaluator {
/**
* Evaluate all checks against a response
* @param checks Array of checks to evaluate
* @param response Response data
* @returns Array of check results
*/
evaluateChecks(checks, response) {
return checks.map(check => this.evaluateCheck(check, response));
}
/**
* Evaluate a single check against a response
* @param check Check to evaluate
* @param response Response data
* @returns Check result
*/
evaluateCheck(check, response) {
switch (check.type) {
case 'status_code':
return this.evaluateStatusCodeCheck(check, response);
case 'header':
return this.evaluateHeaderCheck(check, response);
case 'body':
return this.evaluateBodyCheck(check, response);
case 'jsonpath':
return this.evaluateJsonPathCheck(check, response);
case 'response_time':
return this.evaluateResponseTimeCheck(check, response);
default:
return {
type: 'unknown',
description: 'Unknown check type',
passed: false,
error: `Unknown check type: ${check.type}`
};
}
}
/**
* Evaluate a status code check
* @param check Status code check
* @param response Response data
* @returns Check result
*/
evaluateStatusCodeCheck(check, response) {
const { statusCode } = response;
const { operator, value } = check;
let passed = false;
let error = '';
try {
switch (operator) {
case 'equals':
// Handle multiple status codes separated by |
const codes = value.split('|').map(code => parseInt(code.trim(), 10));
passed = codes.includes(statusCode);
break;
case 'not_equals':
const notCodes = value.split('|').map(code => parseInt(code.trim(), 10));
passed = !notCodes.includes(statusCode);
break;
case 'matches':
const regex = new RegExp(value);
passed = regex.test(statusCode.toString());
break;
case 'not_matches':
const notRegex = new RegExp(value);
passed = !notRegex.test(statusCode.toString());
break;
}
}
catch (err) {
error = err instanceof Error ? err.message : String(err);
passed = false;
}
return {
type: 'status_code',
description: `Status code ${operator} ${value}`,
passed,
error: error || undefined,
actualValue: statusCode,
expectedValue: value
};
}
/**
* Evaluate a header check
* @param check Header check
* @param response Response data
* @returns Check result
*/
evaluateHeaderCheck(check, response) {
const { headers } = response;
const { name, operator, value } = check;
// Headers are case-insensitive
const headerName = name.toLowerCase();
// Find the header value
let headerValue;
for (const [key, val] of Object.entries(headers)) {
if (key.toLowerCase() === headerName) {
headerValue = val;
break;
}
}
let passed = false;
let error = '';
try {
switch (operator) {
case 'exists':
passed = headerValue !== undefined;
break;
case 'not_exists':
passed = headerValue === undefined;
break;
case 'equals':
passed = headerValue === value;
break;
case 'not_equals':
passed = headerValue !== value;
break;
case 'contains':
passed = headerValue !== undefined && headerValue.includes(value || '');
break;
case 'not_contains':
passed = headerValue === undefined || !headerValue.includes(value || '');
break;
case 'matches':
const regex = new RegExp(value || '');
passed = headerValue !== undefined && regex.test(headerValue);
break;
case 'not_matches':
const notRegex = new RegExp(value || '');
passed = headerValue === undefined || !notRegex.test(headerValue);
break;
}
}
catch (err) {
error = err instanceof Error ? err.message : String(err);
passed = false;
}
return {
type: 'header',
description: `Header ${name} ${operator} ${value || ''}`,
passed,
error: error || undefined,
actualValue: headerValue,
expectedValue: value
};
}
/**
* Evaluate a body check
* @param check Body check
* @param response Response data
* @returns Check result
*/
evaluateBodyCheck(check, response) {
const { body } = response;
const { operator, value } = check;
let passed = false;
let error = '';
try {
switch (operator) {
case 'exists':
passed = body !== undefined && body.length > 0;
break;
case 'not_exists':
passed = body === undefined || body.length === 0;
break;
case 'equals':
passed = body === value;
break;
case 'not_equals':
passed = body !== value;
break;
case 'contains':
passed = body.includes(value || '');
break;
case 'not_contains':
passed = !body.includes(value || '');
break;
case 'matches':
const regex = new RegExp(value || '');
passed = regex.test(body);
break;
case 'not_matches':
const notRegex = new RegExp(value || '');
passed = !notRegex.test(body);
break;
}
}
catch (err) {
error = err instanceof Error ? err.message : String(err);
passed = false;
}
return {
type: 'body',
description: `Body ${operator} ${value || ''}`,
passed,
error: error || undefined,
actualValue: body.length > 100 ? `${body.substring(0, 100)}...` : body,
expectedValue: value
};
}
/**
* Evaluate a JSON path check
* @param check JSON path check
* @param response Response data
* @returns Check result
*/
evaluateJsonPathCheck(check, response) {
const { body } = response;
const { path, operator, value, element = 'first' } = check;
let passed = false;
let error = '';
let actualValue = null;
try {
// Parse JSON body
const jsonBody = JSON.parse(body);
// Extract value using JSON path
const extractedValue = this.extractJsonPath(jsonBody, path);
// Handle array results based on element parameter
if (Array.isArray(extractedValue)) {
if (typeof element === 'number') {
// Get specific index
actualValue = extractedValue[element];
}
else if (element === 'first') {
actualValue = extractedValue[0];
}
else if (element === 'last') {
actualValue = extractedValue[extractedValue.length - 1];
}
else if (element === 'any' || element === 'all') {
// For 'any' and 'all', we'll evaluate each element
const results = extractedValue.map(item => this.compareValues(item, operator, value));
passed = element === 'any'
? results.some(result => result)
: results.every(result => result);
actualValue = extractedValue;
return {
type: 'jsonpath',
description: `JSON path ${path} ${element} ${operator} ${value || ''}`,
passed,
error: error || undefined,
actualValue: JSON.stringify(actualValue),
expectedValue: value
};
}
}
else {
actualValue = extractedValue;
}
// For contains/not_contains with objects, convert to string
if ((operator === 'contains' || operator === 'not_contains') &&
typeof actualValue === 'object' && actualValue !== null) {
actualValue = JSON.stringify(actualValue);
}
// Compare the extracted value
passed = this.compareValues(actualValue, operator, value);
}
catch (err) {
error = err instanceof Error ? err.message : String(err);
passed = false;
}
return {
type: 'jsonpath',
description: `JSON path ${path} ${operator} ${value || ''}`,
passed,
error: error || undefined,
actualValue: typeof actualValue === 'object' ? JSON.stringify(actualValue) : actualValue,
expectedValue: value
};
}
/**
* Evaluate a response time check
* @param check Response time check
* @param response Response data
* @returns Check result
*/
evaluateResponseTimeCheck(check, response) {
const { responseTime } = response;
const { operator, value } = check;
let passed = false;
switch (operator) {
case 'less_than':
passed = responseTime < value;
break;
case 'greater_than':
passed = responseTime > value;
break;
}
return {
type: 'response_time',
description: `Response time ${operator} ${value}ms`,
passed,
actualValue: responseTime,
expectedValue: value
};
}
/**
* Extract a value from a JSON object using a JSON path expression
* @param json JSON object
* @param path JSON path expression
* @returns Extracted value
*/
extractJsonPath(json, path) {
// Simple JSON path implementation
// Supports basic paths like $.name, $[0].name, etc.
// Remove leading $ if present
const normalizedPath = path.startsWith('$') ? path.substring(1) : path;
// Split path into segments
const segments = normalizedPath
.replace(/\[(\w+)\]/g, '.$1') // Convert [0] to .0
.replace(/^\./, '') // Remove leading dot
.split('.');
// Traverse the object
let current = json;
for (const segment of segments) {
if (current === null || current === undefined) {
return undefined;
}
current = current[segment];
}
return current;
}
/**
* Compare values using the specified operator
* @param actual Actual value
* @param operator Comparison operator
* @param expected Expected value
* @returns Whether the comparison passed
*/
compareValues(actual, operator, expected) {
switch (operator) {
case 'exists':
return actual !== undefined && actual !== null;
case 'not_exists':
return actual === undefined || actual === null;
case 'equals':
return String(actual) === expected;
case 'not_equals':
return String(actual) !== expected;
case 'contains':
return String(actual).includes(expected || '');
case 'not_contains':
return !String(actual).includes(expected || '');
case 'matches':
const regex = new RegExp(expected || '');
return regex.test(String(actual));
case 'not_matches':
const notRegex = new RegExp(expected || '');
return !notRegex.test(String(actual));
case 'greater_than':
return Number(actual) > Number(expected);
case 'less_than':
return Number(actual) < Number(expected);
default:
return false;
}
}
}
exports.CheckEvaluator = CheckEvaluator;
//# sourceMappingURL=evaluator.js.map