@catbee/utils
Version:
A modular, production-grade utility toolkit for Node.js and TypeScript, designed for robust, scalable applications (including Express-based services). All utilities are tree-shakable and can be imported independently.
208 lines • 8.11 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RedirectResponse = exports.NoContentResponse = exports.PaginatedResponse = exports.ErrorResponse = exports.SuccessResponse = void 0;
exports.createSuccessResponse = createSuccessResponse;
exports.createErrorResponse = createErrorResponse;
exports.createPaginatedResponse = createPaginatedResponse;
exports.sendResponse = sendResponse;
const crypto_1 = require("crypto");
const context_store_utils_1 = require("./context-store.utils");
/**
* Standard HTTP response wrapper for successful responses.
* Implements the `ApiResponse<T>` interface and sets default values.
*
* @typeParam T - The shape of the data returned in the response.
*/
class SuccessResponse {
/**
* Constructs a new success response.
*
* @param {string} message - Optional message to override the default.
* @param {T} [data] - Optional data payload.
*/
constructor(message, data) {
var _a;
/** Message describing the result of the operation. */
this.message = "Success";
/** Whether the response is an error. Always false in success responses. */
this.error = false;
/** The payload returned from the API. */
this.data = null;
/** Timestamp when the response was generated, in ISO format. */
this.timestamp = new Date().toISOString();
/** Unique identifier for this response, useful for request tracing. */
this.requestId = (_a = (0, context_store_utils_1.getRequestId)()) !== null && _a !== void 0 ? _a : (0, crypto_1.randomUUID)();
if (message)
this.message = message;
this.data = data !== null && data !== void 0 ? data : null;
}
}
exports.SuccessResponse = SuccessResponse;
/**
* Wrapper for error responses that extends the native `Error` object.
* Implements `ApiResponse` but omits the `data` field (which should not be present in errors).
*/
class ErrorResponse extends Error {
/**
* Constructs a new error response.
*
* @param {string} message - The error message to display or log.
* @param {number} [status=500] - Optional HTTP status code (defaults to 500).
*/
constructor(message, status = 500) {
var _a;
super(message);
/** Indicates that this is an error. Always true. */
this.error = true;
/** Timestamp when the error occurred, in ISO format. */
this.timestamp = new Date().toISOString();
/** Unique identifier for this error instance. */
this.requestId = (_a = (0, context_store_utils_1.getRequestId)()) !== null && _a !== void 0 ? _a : (0, crypto_1.randomUUID)();
this.status = status;
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
}
}
exports.ErrorResponse = ErrorResponse;
/**
* Response with paginated data extending the standard success response.
* Useful for APIs that return large collections of data.
*
* @typeParam T - The shape of each item in the paginated collection.
*/
class PaginatedResponse extends SuccessResponse {
/**
* Constructs a new paginated response.
*
* @param {T[]} items - The current page of items.
* @param {Object} pagination - Pagination information.
* @param {number} pagination.total - Total number of items across all pages.
* @param {number} pagination.page - Current page number (1-based).
* @param {number} pagination.pageSize - Number of items per page.
* @param {string} [message="Success"] - Optional custom message.
*/
constructor(items, pagination, message = "Success") {
super(message, items);
this.total = pagination.total;
this.page = pagination.page;
this.pageSize = pagination.pageSize;
this.totalPages = Math.ceil(this.total / this.pageSize);
this.hasNext = this.page < this.totalPages;
this.hasPrevious = this.page > 1;
}
}
exports.PaginatedResponse = PaginatedResponse;
/**
* Specialized response for operations that don't return data (HTTP 204).
*/
class NoContentResponse extends SuccessResponse {
/**
* Constructs a new no-content response.
*
* @param {string} [message="Operation completed successfully"] - Optional custom message.
*/
constructor(message = "Operation completed successfully") {
super(message, null);
}
}
exports.NoContentResponse = NoContentResponse;
/**
* Specialized response for redirects.
*/
class RedirectResponse {
/**
* Constructs a new redirect response.
*
* @param {string} url - The URL to redirect to.
* @param {number} [statusCode=302] - HTTP status code for the redirect.
*/
constructor(url, statusCode = 302) {
var _a;
/** Whether the response is a redirect */
this.isRedirect = true;
/** Unique identifier for this response */
this.requestId = (_a = (0, context_store_utils_1.getRequestId)()) !== null && _a !== void 0 ? _a : (0, crypto_1.randomUUID)();
this.redirectUrl = url;
this.statusCode = statusCode;
}
}
exports.RedirectResponse = RedirectResponse;
/**
* Creates a standard success response.
*
* @typeParam T - The shape of the data returned in the response.
* @param {T} data - The data to include in the response.
* @param {string} [message="Success"] - Optional custom message.
* @returns {SuccessResponse<T>} A properly formatted success response.
*/
function createSuccessResponse(data, message = "Success") {
return new SuccessResponse(message, data);
}
/**
* Creates a standard error response.
*
* @param {string} message - The error message.
* @param {number} [statusCode=500] - HTTP status code for the error.
* @returns {ErrorResponse} A properly formatted error response.
*/
function createErrorResponse(message, statusCode = 500) {
return new ErrorResponse(message, statusCode);
}
/**
* Creates a paginated response from array data.
*
* @typeParam T - The shape of each item in the collection.
* @param {T[]} allItems - The complete array of items to paginate.
* @param {number} page - The requested page (1-based).
* @param {number} pageSize - The number of items per page.
* @param {string} [message="Success"] - Optional custom message.
* @returns {PaginatedResponse<T>} A properly formatted paginated response.
*/
function createPaginatedResponse(allItems, page, pageSize, message = "Success") {
// Ensure valid pagination parameters
const validPage = Math.max(1, page);
const validPageSize = Math.max(1, pageSize);
// Calculate slice indices
const startIndex = (validPage - 1) * validPageSize;
const endIndex = startIndex + validPageSize;
// Get current page items
const pageItems = allItems.slice(startIndex, endIndex);
return new PaginatedResponse(pageItems, {
total: allItems.length,
page: validPage,
pageSize: validPageSize,
}, message);
}
/**
* Adapter to convert API responses to Express.js response format.
*
* @param {any} res - Express response object.
* @param {SuccessResponse<any> | ErrorResponse | RedirectResponse} apiResponse - API response instance.
*/
function sendResponse(res, apiResponse) {
// Handle redirects
if ("redirectUrl" in apiResponse) {
res.redirect(apiResponse.statusCode, apiResponse.redirectUrl);
return;
}
// Handle errors
if ("error" in apiResponse && apiResponse.error) {
const errorResponse = apiResponse;
res.status(errorResponse.status).json({
error: true,
message: errorResponse.message,
timestamp: errorResponse.timestamp,
requestId: errorResponse.requestId,
});
return;
}
// Handle success responses
const successResponse = apiResponse;
// No content (204) shouldn't return a body
if (successResponse instanceof NoContentResponse) {
res.status(204).end();
return;
}
res.status(200).json(successResponse);
}
//# sourceMappingURL=response.utils.js.map