@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.
235 lines (229 loc) • 8.24 kB
JavaScript
/*
* The MIT License
*
* Copyright (c) 2026 Catbee Technologies. https://catbee.in/license
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
'use strict';
var contextStore = require('@catbee/utils/context-store');
var env = require('@catbee/utils/env');
var id = require('@catbee/utils/id');
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var SuccessResponse = class {
static {
__name(this, "SuccessResponse");
}
/** Message describing the result of the operation. */
message = "Success";
/** Whether the response is an error. Always false in success responses. */
error = false;
/** The payload returned from the API. */
data = null;
/** Timestamp when the response was generated, in ISO format. */
timestamp = (/* @__PURE__ */ new Date()).toISOString();
/** Unique identifier for this response, useful for request tracing. */
requestId = contextStore.getRequestId() ?? id.uuid();
/**
* Constructs a new success response.
*
* @param {string} message - Optional message to override the default.
* @param {T} [data] - Optional data payload.
*/
constructor(message, data) {
if (message) this.message = message;
this.data = data ?? null;
}
};
var ErrorResponse = class extends Error {
static {
__name(this, "ErrorResponse");
}
/** HTTP status code associated with the error (e.g., 404, 500). */
status;
/** Indicates that this is an error. Always true. */
error = true;
/** Timestamp when the error occurred, in ISO format. */
timestamp = (/* @__PURE__ */ new Date()).toISOString();
/** Unique identifier for this error instance. */
requestId = contextStore.getRequestId() ?? id.uuid();
/**
* 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) {
super(message);
this.status = status;
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
}
};
var PaginatedResponse = class extends SuccessResponse {
static {
__name(this, "PaginatedResponse");
}
/** Total number of items across all pages */
total;
/** Current page number (1-based) */
page;
/** Number of items per page */
pageSize;
/** Total number of pages */
totalPages;
/** Whether there's a next page available */
hasNext;
/** Whether there's a previous page available */
hasPrevious;
/**
* 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;
}
};
var NoContentResponse = class extends SuccessResponse {
static {
__name(this, "NoContentResponse");
}
/**
* Constructs a new no-content response.
*
* @param {string} [message="Operation completed successfully"] - Optional custom message.
*/
constructor(message = "Operation completed successfully") {
super(message, null);
}
};
var RedirectResponse = class {
static {
__name(this, "RedirectResponse");
}
/** The URL to redirect to */
redirectUrl;
/** HTTP status code for the redirect (301, 302, 303, 307, 308) */
statusCode;
/** Whether the response is a redirect */
isRedirect = true;
/** Unique identifier for this response */
requestId = contextStore.getRequestId() ?? id.uuid();
/**
* 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) {
this.redirectUrl = url;
this.statusCode = statusCode;
}
};
function createSuccessResponse(data, message = "Success") {
return new SuccessResponse(message, data);
}
__name(createSuccessResponse, "createSuccessResponse");
function createErrorResponse(message, statusCode = 500) {
return new ErrorResponse(message, statusCode);
}
__name(createErrorResponse, "createErrorResponse");
function createFinalErrorResponse(req, status, message, error, options) {
const isDev = env.Env.isDev();
const includeDetails = options?.includeDetails && isDev;
const response = {
error: true,
message,
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
requestId: error?.requestId || req?.id || id.uuid(),
status,
path: req.originalUrl || req.url
};
if (includeDetails && error?.stack) {
response.stack = error.stack.split("\n").map((line) => line.trim());
}
return response;
}
__name(createFinalErrorResponse, "createFinalErrorResponse");
function createPaginatedResponse(allItems, page, pageSize, message = "Success") {
const validPage = Math.max(1, page);
const validPageSize = Math.max(1, pageSize);
const startIndex = (validPage - 1) * validPageSize;
const endIndex = startIndex + validPageSize;
const pageItems = allItems.slice(startIndex, endIndex);
return new PaginatedResponse(pageItems, {
total: allItems.length,
page: validPage,
pageSize: validPageSize
}, message);
}
__name(createPaginatedResponse, "createPaginatedResponse");
function sendResponse(res, apiResponse) {
if ("redirectUrl" in apiResponse) {
res.redirect(apiResponse.statusCode, apiResponse.redirectUrl);
return;
}
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;
}
const successResponse = apiResponse;
if (successResponse instanceof NoContentResponse) {
res.status(204).end();
return;
}
res.status(200).json(successResponse);
}
__name(sendResponse, "sendResponse");
// src/types/api-response.ts
var SortDirection = /* @__PURE__ */ (function(SortDirection2) {
SortDirection2["ASC"] = "asc";
SortDirection2["DESC"] = "desc";
return SortDirection2;
})({});
exports.ErrorResponse = ErrorResponse;
exports.NoContentResponse = NoContentResponse;
exports.PaginatedResponse = PaginatedResponse;
exports.RedirectResponse = RedirectResponse;
exports.SortDirection = SortDirection;
exports.SuccessResponse = SuccessResponse;
exports.createErrorResponse = createErrorResponse;
exports.createFinalErrorResponse = createFinalErrorResponse;
exports.createPaginatedResponse = createPaginatedResponse;
exports.createSuccessResponse = createSuccessResponse;
exports.sendResponse = sendResponse;