Governed substrate for autonomous agents: scoped identity (passports), audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
177 lines
4.4 KiB
JavaScript
177 lines
4.4 KiB
JavaScript
// backend/utils/response.js
|
|
// Response Helpers - ADR-015
|
|
// Created: 2026-01-08
|
|
|
|
/**
|
|
* Send success response
|
|
* @param {import('express').Response} res
|
|
* @param {any} data - Response data
|
|
* @param {string|number} messageOrStatus - Message string or HTTP status code (default: 200)
|
|
* @param {number} statusCode - HTTP status code when message provided (default: 200)
|
|
*/
|
|
export function success(res, data, messageOrStatus = 200, statusCode = 200) {
|
|
let message = null;
|
|
let status = 200;
|
|
|
|
if (typeof messageOrStatus === 'string') {
|
|
message = messageOrStatus;
|
|
status = statusCode;
|
|
} else if (typeof messageOrStatus === 'number') {
|
|
status = messageOrStatus;
|
|
}
|
|
|
|
const response = {
|
|
success: true,
|
|
data,
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
|
|
if (message) {
|
|
response.message = message;
|
|
}
|
|
|
|
return res.status(status).json(response);
|
|
}
|
|
|
|
/**
|
|
* Send created response (201)
|
|
* @param {import('express').Response} res
|
|
* @param {any} data
|
|
* @param {string} message - Optional message
|
|
*/
|
|
export function created(res, data, message = null) {
|
|
if (message) {
|
|
return success(res, data, message, 201);
|
|
}
|
|
return success(res, data, 201);
|
|
}
|
|
|
|
/**
|
|
* Send no content response (204)
|
|
* @param {import('express').Response} res
|
|
*/
|
|
export function noContent(res) {
|
|
return res.status(204).end();
|
|
}
|
|
|
|
/**
|
|
* Send error response
|
|
* @param {import('express').Response} res
|
|
* @param {string} code - Error code
|
|
* @param {string} message - Error message
|
|
* @param {number} statusCode - HTTP status code (default: 500)
|
|
* @param {any} details - Additional error details
|
|
*/
|
|
export function error(res, code, message, statusCode = 500, details = null) {
|
|
const response = {
|
|
success: false,
|
|
error: { code, message },
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
|
|
if (details) {
|
|
response.error.details = details;
|
|
}
|
|
|
|
return res.status(statusCode).json(response);
|
|
}
|
|
|
|
/**
|
|
* Send bad request (400)
|
|
* @param {import('express').Response} res
|
|
* @param {string} message
|
|
* @param {string} code
|
|
*/
|
|
export function badRequest(res, message, code = 'BAD_REQUEST') {
|
|
return error(res, code, message, 400);
|
|
}
|
|
|
|
/**
|
|
* Send unauthorized (401)
|
|
* @param {import('express').Response} res
|
|
* @param {string} message
|
|
*/
|
|
export function unauthorized(res, message = 'Unauthorized') {
|
|
return error(res, 'UNAUTHORIZED', message, 401);
|
|
}
|
|
|
|
/**
|
|
* Send forbidden (403)
|
|
* @param {import('express').Response} res
|
|
* @param {string} message
|
|
*/
|
|
export function forbidden(res, message = 'Forbidden') {
|
|
return error(res, 'FORBIDDEN', message, 403);
|
|
}
|
|
|
|
/**
|
|
* Send not found (404)
|
|
* @param {import('express').Response} res
|
|
* @param {string} resource - Resource type that was not found
|
|
*/
|
|
export function notFound(res, resource = 'Resource') {
|
|
return error(res, 'NOT_FOUND', `${resource} not found`, 404);
|
|
}
|
|
|
|
/**
|
|
* Send conflict (409)
|
|
* @param {import('express').Response} res
|
|
* @param {string} message
|
|
*/
|
|
export function conflict(res, message = 'Resource already exists') {
|
|
return error(res, 'CONFLICT', message, 409);
|
|
}
|
|
|
|
/**
|
|
* Send unprocessable entity (422)
|
|
* @param {import('express').Response} res
|
|
* @param {string} message
|
|
* @param {any} details
|
|
*/
|
|
export function unprocessable(res, message, details = null) {
|
|
return error(res, 'UNPROCESSABLE_ENTITY', message, 422, details);
|
|
}
|
|
|
|
/**
|
|
* Send internal server error (500)
|
|
* @param {import('express').Response} res
|
|
* @param {string} message
|
|
*/
|
|
export function serverError(res, message = 'Internal server error') {
|
|
return error(res, 'INTERNAL_ERROR', message, 500);
|
|
}
|
|
|
|
/**
|
|
* Send paginated response
|
|
* @param {import('express').Response} res
|
|
* @param {Array} data - Array of items
|
|
* @param {Object} pagination - Pagination info
|
|
* @param {number} pagination.page - Current page
|
|
* @param {number} pagination.limit - Items per page
|
|
* @param {number} pagination.total - Total items count
|
|
*/
|
|
export function paginated(res, data, pagination) {
|
|
const { page, limit, total } = pagination;
|
|
const totalPages = Math.ceil(total / limit);
|
|
|
|
return res.json({
|
|
success: true,
|
|
data,
|
|
pagination: {
|
|
page,
|
|
limit,
|
|
total,
|
|
totalPages
|
|
},
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Send accepted response (202) for async operations
|
|
* @param {import('express').Response} res
|
|
* @param {Object} data - Contains tracking info like jobId
|
|
*/
|
|
export function accepted(res, data) {
|
|
return success(res, data, 202);
|
|
}
|