@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.
561 lines • 22.2 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
import fs from "fs/promises";
import { createReadStream, createWriteStream } from "fs";
import { tmpdir } from "os";
import path from "path";
import { pipeline } from "stream/promises";
import { randomUUID } from "crypto";
/**
* Checks whether a file or directory exists at the given path.
*
* @param {string} path - The file or directory path to check.
* @returns {Promise<boolean>} Resolves to `true` if the path exists, `false` otherwise.
*/
export function fileExists(path) {
return __awaiter(this, void 0, void 0, function () {
var _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_b.trys.push([0, 2, , 3]);
return [4 /*yield*/, fs.access(path)];
case 1:
_b.sent();
return [2 /*return*/, true];
case 2:
_a = _b.sent();
return [2 /*return*/, false];
case 3: return [2 /*return*/];
}
});
});
}
/**
* Reads and parses a JSON file from the specified path.
*
* @typeParam T - The expected type of the parsed JSON object.
* @param {string} path - The path to the JSON file.
* @returns {Promise<T | null>} The parsed object, or `null` if reading or parsing fails.
*/
export function readJsonFile(path) {
return __awaiter(this, void 0, void 0, function () {
var data, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_b.trys.push([0, 2, , 3]);
return [4 /*yield*/, fs.readFile(path, "utf-8")];
case 1:
data = _b.sent();
return [2 /*return*/, JSON.parse(data)];
case 2:
_a = _b.sent();
return [2 /*return*/, null];
case 3: return [2 /*return*/];
}
});
});
}
/**
* Writes a JavaScript object to a file as formatted JSON.
*
* @param {string} path - The destination file path.
* @param {any} data - The object to serialize and write.
* @param {number} [space=2] - Number of spaces for JSON formatting.
* @returns {Promise<void>} Resolves when writing is complete.
*/
export function writeJsonFile(path_1, data_1) {
return __awaiter(this, arguments, void 0, function (path, data, space) {
var json;
if (space === void 0) { space = 2; }
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
json = JSON.stringify(data, null, space);
return [4 /*yield*/, fs.writeFile(path, json, "utf-8")];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
}
/**
* Deletes a file if it exists.
*
* @param {string} path - The file path to delete.
* @returns {Promise<boolean>} Resolves to `true` if the file was deleted or didn't exist, `false` if deletion failed.
*/
export function deleteFileIfExists(path) {
return __awaiter(this, void 0, void 0, function () {
var err_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
return [4 /*yield*/, fs.unlink(path)];
case 1:
_a.sent();
return [2 /*return*/, true];
case 2:
err_1 = _a.sent();
if (err_1.code === "ENOENT")
return [2 /*return*/, true];
return [2 /*return*/, false];
case 3: return [2 /*return*/];
}
});
});
}
/**
* Reads a text file from the specified path.
*
* @param {string} path - The path to the text file.
* @param {BufferEncoding} [encoding="utf-8"] - The encoding to use.
* @returns {Promise<string | null>} The file contents, or `null` if reading fails.
*/
export function readTextFile(path_1) {
return __awaiter(this, arguments, void 0, function (path, encoding) {
var _a;
if (encoding === void 0) { encoding = "utf-8"; }
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_b.trys.push([0, 2, , 3]);
return [4 /*yield*/, fs.readFile(path, encoding)];
case 1: return [2 /*return*/, _b.sent()];
case 2:
_a = _b.sent();
return [2 /*return*/, null];
case 3: return [2 /*return*/];
}
});
});
}
/**
* Writes text content to a file.
*
* @param {string} path - The destination file path.
* @param {string} content - The text content to write.
* @param {BufferEncoding} [encoding="utf-8"] - The encoding to use.
* @returns {Promise<boolean>} Resolves to `true` if successful, `false` otherwise.
*/
export function writeTextFile(path_1, content_1) {
return __awaiter(this, arguments, void 0, function (path, content, encoding) {
var _a;
if (encoding === void 0) { encoding = "utf-8"; }
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_b.trys.push([0, 2, , 3]);
return [4 /*yield*/, fs.writeFile(path, content, encoding)];
case 1:
_b.sent();
return [2 /*return*/, true];
case 2:
_a = _b.sent();
return [2 /*return*/, false];
case 3: return [2 /*return*/];
}
});
});
}
/**
* Appends text content to a file.
*
* @param {string} path - The file path to append to.
* @param {string} content - The text content to append.
* @param {BufferEncoding} [encoding="utf-8"] - The encoding to use.
* @returns {Promise<boolean>} Resolves to `true` if successful, `false` otherwise.
*/
export function appendTextFile(path_1, content_1) {
return __awaiter(this, arguments, void 0, function (path, content, encoding) {
var _a;
if (encoding === void 0) { encoding = "utf-8"; }
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_b.trys.push([0, 2, , 3]);
return [4 /*yield*/, fs.appendFile(path, content, encoding)];
case 1:
_b.sent();
return [2 /*return*/, true];
case 2:
_a = _b.sent();
return [2 /*return*/, false];
case 3: return [2 /*return*/];
}
});
});
}
/**
* Copies a file from source to destination.
*
* @param {string} source - Source file path.
* @param {string} destination - Destination file path.
* @param {boolean} [overwrite=false] - Whether to overwrite if destination exists.
* @returns {Promise<boolean>} Resolves to `true` if successful, `false` otherwise.
*/
export function copyFile(source_1, destination_1) {
return __awaiter(this, arguments, void 0, function (source, destination, overwrite) {
var flags, _a;
if (overwrite === void 0) { overwrite = false; }
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_b.trys.push([0, 2, , 3]);
flags = overwrite ? 0 : fs.constants.COPYFILE_EXCL;
return [4 /*yield*/, fs.copyFile(source, destination, flags)];
case 1:
_b.sent();
return [2 /*return*/, true];
case 2:
_a = _b.sent();
return [2 /*return*/, false];
case 3: return [2 /*return*/];
}
});
});
}
/**
* Renames/moves a file.
*
* @param {string} oldPath - Current file path.
* @param {string} newPath - New file path.
* @returns {Promise<boolean>} Resolves to `true` if successful, `false` otherwise.
*/
export function moveFile(oldPath, newPath) {
return __awaiter(this, void 0, void 0, function () {
var _a, _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
_c.trys.push([0, 2, , 8]);
return [4 /*yield*/, fs.rename(oldPath, newPath)];
case 1:
_c.sent();
return [2 /*return*/, true];
case 2:
_a = _c.sent();
_c.label = 3;
case 3:
_c.trys.push([3, 6, , 7]);
return [4 /*yield*/, fs.copyFile(oldPath, newPath)];
case 4:
_c.sent();
return [4 /*yield*/, fs.unlink(oldPath)];
case 5:
_c.sent();
return [2 /*return*/, true];
case 6:
_b = _c.sent();
return [2 /*return*/, false];
case 7: return [3 /*break*/, 8];
case 8: return [2 /*return*/];
}
});
});
}
/**
* Gets file stats if the file exists.
*
* @param {string} path - Path to the file.
* @returns {Promise<fs.Stats | null>} File stats object or null if file doesn't exist.
*/
export function getFileStats(path) {
return __awaiter(this, void 0, void 0, function () {
var _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_b.trys.push([0, 2, , 3]);
return [4 /*yield*/, fs.stat(path)];
case 1: return [2 /*return*/, _b.sent()];
case 2:
_a = _b.sent();
return [2 /*return*/, null];
case 3: return [2 /*return*/];
}
});
});
}
/**
* Creates a temporary file with optional content.
*
* @param {object} [options] - Options for creating the temp file.
* @param {string} [options.prefix="tmp-"] - Filename prefix.
* @param {string} [options.extension=""] - File extension.
* @param {string} [options.dir] - Directory to create the file in (defaults to OS temp dir).
* @param {string | Buffer} [options.content] - Optional content to write to the file.
* @returns {Promise<string>} Path to the created temporary file.
*/
export function createTempFile() {
return __awaiter(this, arguments, void 0, function (_a) {
var ext, filename, filepath;
var _b = _a === void 0 ? {} : _a, _c = _b.prefix, prefix = _c === void 0 ? "tmp-" : _c, _d = _b.extension, extension = _d === void 0 ? "" : _d, _e = _b.dir, dir = _e === void 0 ? tmpdir() : _e, content = _b.content;
return __generator(this, function (_f) {
switch (_f.label) {
case 0:
ext = extension
? extension.startsWith(".")
? extension
: ".".concat(extension)
: "";
filename = "".concat(prefix).concat(randomUUID()).concat(ext);
filepath = path.join(dir, filename);
if (!content) return [3 /*break*/, 2];
return [4 /*yield*/, fs.writeFile(filepath, content)];
case 1:
_f.sent();
return [3 /*break*/, 4];
case 2: return [4 /*yield*/, fs.writeFile(filepath, "")];
case 3:
_f.sent();
_f.label = 4;
case 4: return [2 /*return*/, filepath];
}
});
});
}
/**
* Streams a file from source to destination.
* Useful for large files to avoid loading the entire file into memory.
*
* @param {string} source - Source file path.
* @param {string} destination - Destination file path.
* @returns {Promise<void>} Resolves when streaming completes.
* @throws {Error} If streaming fails.
*/
export function streamFile(source, destination) {
return __awaiter(this, void 0, void 0, function () {
var readStream, writeStream, error_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
readStream = createReadStream(source);
writeStream = createWriteStream(destination);
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, pipeline(readStream, writeStream)];
case 2:
_a.sent();
return [3 /*break*/, 4];
case 3:
error_1 = _a.sent();
// Ensure both streams are closed in case of error
readStream.destroy();
writeStream.destroy();
throw error_1;
case 4: return [2 /*return*/];
}
});
});
}
/**
* Reads a directory and returns file names.
*
* @param {string} dirPath - Path to the directory.
* @param {object} [options] - Options for reading the directory.
* @param {boolean} [options.fullPaths=false] - Whether to return full paths.
* @param {RegExp} [options.filter] - Optional regex to filter files.
* @returns {Promise<string[]>} Array of file names or paths.
* @throws {Error} If directory cannot be read.
*/
export function readDirectory(dirPath_1) {
return __awaiter(this, arguments, void 0, function (dirPath, options) {
var files, result;
if (options === void 0) { options = {}; }
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, fs.readdir(dirPath)];
case 1:
files = _a.sent();
result = files;
if (options.filter) {
result = result.filter(function (file) { return options.filter.test(file); });
}
if (options.fullPaths) {
result = result.map(function (file) { return path.join(dirPath, file); });
}
return [2 /*return*/, result];
}
});
});
}
/**
* Creates a directory if it doesn't exist.
*
* @param {string} dirPath - Path to the directory.
* @param {boolean} [recursive=true] - Whether to create parent directories.
* @returns {Promise<boolean>} Resolves to `true` if successful, `false` otherwise.
*/
export function createDirectory(dirPath_1) {
return __awaiter(this, arguments, void 0, function (dirPath, recursive) {
var err_2;
if (recursive === void 0) { recursive = true; }
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
return [4 /*yield*/, fs.mkdir(dirPath, { recursive: recursive })];
case 1:
_a.sent();
return [2 /*return*/, true];
case 2:
err_2 = _a.sent();
// Consider it success if directory already exists
return [2 /*return*/, err_2.code === "EEXIST"];
case 3: return [2 /*return*/];
}
});
});
}
/**
* Safely reads and parses a JSON file with error details.
*
* @typeParam T - The expected type of the parsed JSON object.
* @param {string} path - The path to the JSON file.
* @returns {Promise<{ data: T | null; error: Error | null }>} Object with data and error properties.
*/
export function safeReadJsonFile(path) {
return __awaiter(this, void 0, void 0, function () {
var content, data, error_2;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
return [4 /*yield*/, fs.readFile(path, "utf-8")];
case 1:
content = _a.sent();
try {
data = JSON.parse(content);
return [2 /*return*/, { data: data, error: null }];
}
catch (error) {
return [2 /*return*/, {
data: null,
error: new Error("Invalid JSON in file ".concat(path, ": ").concat(error.message)),
}];
}
return [3 /*break*/, 3];
case 2:
error_2 = _a.sent();
return [2 /*return*/, {
data: null,
error: new Error("Failed to read file ".concat(path, ": ").concat(error_2.message)),
}];
case 3: return [2 /*return*/];
}
});
});
}
/**
* Checks if a path points to a file (not a directory).
*
* @param {string} path - Path to check.
* @returns {Promise<boolean>} True if the path is a file, false otherwise.
*/
export function isFile(path) {
return __awaiter(this, void 0, void 0, function () {
var stat, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_b.trys.push([0, 2, , 3]);
return [4 /*yield*/, fs.stat(path)];
case 1:
stat = _b.sent();
return [2 /*return*/, stat.isFile()];
case 2:
_a = _b.sent();
return [2 /*return*/, false];
case 3: return [2 /*return*/];
}
});
});
}
/**
* Gets the size of a file in bytes.
*
* @param {string} path - Path to the file.
* @returns {Promise<number>} Size in bytes or -1 if file doesn't exist.
*/
export function getFileSize(path) {
return __awaiter(this, void 0, void 0, function () {
var stat, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_b.trys.push([0, 2, , 3]);
return [4 /*yield*/, fs.stat(path)];
case 1:
stat = _b.sent();
return [2 /*return*/, stat.size];
case 2:
_a = _b.sent();
return [2 /*return*/, -1];
case 3: return [2 /*return*/];
}
});
});
}
/**
* Reads a file as a Buffer.
*
* @param {string} path - Path to the file.
* @returns {Promise<Buffer | null>} File contents as Buffer or null if reading fails.
*/
export function readFileBuffer(path) {
return __awaiter(this, void 0, void 0, function () {
var _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_b.trys.push([0, 2, , 3]);
return [4 /*yield*/, fs.readFile(path)];
case 1: return [2 /*return*/, _b.sent()];
case 2:
_a = _b.sent();
return [2 /*return*/, null];
case 3: return [2 /*return*/];
}
});
});
}
//# sourceMappingURL=fs.utils.js.map